Bit more consistent but still far from perfect

This commit is contained in:
2026-04-27 09:14:14 -05:00
parent 998601f722
commit 19f2a2c616
21 changed files with 2233 additions and 617 deletions
+12 -69
View File
@@ -18,60 +18,13 @@
#include "console/console.h"
#include "util/string.h"
#include "script/scriptmanager.h"
#include "script/module/scene/modulescene.h"
scene_t SCENE;
// Releases the current scene class ref and rebuilds Scene.set global.
static void sceneReset(void) {
lua_State *L = SCRIPT_MANAGER.mainContext.luaState;
if(SCENE.scriptRef != LUA_NOREF) {
luaL_unref(L, LUA_REGISTRYINDEX, SCENE.scriptRef);
SCENE.scriptRef = LUA_NOREF;
}
lua_newtable(L);
lua_pushcfunction(L, sceneSetLua);
lua_setfield(L, -2, "set");
lua_setglobal(L, "Scene");
}
// Calls sceneClass:method() if it exists. Returns an error if the call fails.
static errorret_t sceneCall(const char_t *method) {
lua_State *L = SCRIPT_MANAGER.mainContext.luaState;
if(SCENE.scriptRef == LUA_NOREF || SCENE.scriptRef == LUA_REFNIL) {
errorOk();
}
lua_rawgeti(L, LUA_REGISTRYINDEX, SCENE.scriptRef);
if(!lua_istable(L, -1)) {
lua_pop(L, 1);
errorThrow("Scene script ref %d is not a table", SCENE.scriptRef);
}
lua_getfield(L, -1, method);
if(!lua_isfunction(L, -1)) {
lua_pop(L, 2);
errorOk();
}
lua_pushvalue(L, -2); // self
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
const char_t *err = lua_tostring(L, -1);
lua_pop(L, 2); // error + scene table
errorThrow("Scene:%s failed: %s", method, err);
}
lua_pop(L, 1); // scene table
errorOk();
}
errorret_t sceneInit(void) {
memoryZero(&SCENE, sizeof(scene_t));
SCENE.scriptRef = LUA_NOREF;
sceneReset();
errorOk();
}
@@ -86,8 +39,8 @@ errorret_t sceneUpdate(void) {
errorChain(sceneSetImmediate(SCENE.sceneNext));
}
if(SCENE.scriptActive) {
errorChain(sceneCall("update"));
if(SCENE.sceneActive) {
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "update"));
}
errorOk();
@@ -209,14 +162,16 @@ errorret_t sceneSetImmediate(const char_t *scene) {
);
}
if(SCENE.scriptActive) {
errorChain(sceneCall("dispose"));
SCENE.scriptActive = false;
if(SCENE.sceneActive) {
errorChain(moduleSceneCall(
SCRIPT_MANAGER.mainContext.luaState, "dispose"
));
SCENE.sceneActive = false;
}
// Wipe Scene back to a clean table before loading the next file so custom
// methods from the previous scene (e.g. Scene.doThing) cannot bleed through.
sceneReset();
moduleSceneReset(SCRIPT_MANAGER.mainContext.luaState);
stringCopy(
SCENE.sceneCurrent,
@@ -240,8 +195,8 @@ errorret_t sceneSetImmediate(const char_t *scene) {
SCENE.scriptRef = luaL_ref(L, LUA_REGISTRYINDEX);
errorChain(sceneCall("init"));
SCENE.scriptActive = true;
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "init"));
SCENE.sceneActive = true;
}
errorOk();
@@ -256,18 +211,6 @@ void sceneSet(const char_t *scene) {
}
errorret_t sceneDispose(void) {
errorChain(sceneSetImmediate(NULL));
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "dispose"));
errorOk();
}
int sceneSetLua(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "Scene.set requires a string argument");
return 0;
}
sceneSet(lua_tostring(L, 1));
return 0;
}
+47 -1
View File
@@ -13,7 +13,7 @@
#define SCENE_EVENT_UPDATE_MAX 16
typedef struct {
bool_t scriptActive;
bool_t sceneActive;
int scriptRef;
char_t sceneCurrent[ASSET_FILE_PATH_MAX];
char_t sceneNext[ASSET_FILE_PATH_MAX];
@@ -21,10 +21,56 @@ typedef struct {
extern scene_t SCENE;
/**
* Initializes the scene manager
*
* @return Any error state that happened.
*/
errorret_t sceneInit(void);
/**
* Ticks the scene manager, may or may not call the scene's update method
* depending on time state.
*
* @return Any error state that happened.
*/
errorret_t sceneUpdate(void);
/**
* Renders the scene, happens regardless of time.
*
* @return Any error state that happened.
*/
errorret_t sceneRender(void);
/**
* Internal method to immediately switch scenes, this will also delete some of
* the lua state, which can cause problems if called during a lua callback, so
* this is managed by the scene manager.
*
* @param scene Scene to switch to (asset file path).
* @return Any error state that happened.
*/
errorret_t sceneSetImmediate(const char_t *scene);
/**
* Requests the scene manager to change scenes when it is next able to. This
* will not guarantee that the scene is valid or without errors.
*
* @param scene Which scene to set.
*/
void sceneSet(const char_t *scene);
/**
* Disposes of the current scene.
* @return Any error state that happened.
*/
errorret_t sceneDispose(void);
/**
* Lua callback when Scene.set is invoked.
*
* @param L Lua state, expects the first argument to be the scene name (string).
* @return 0, does not return any values to Lua.
*/
int sceneSetLua(lua_State *L);
+2 -2
View File
@@ -53,7 +53,7 @@ static int moduleColorToString(lua_State *L) {
return 1;
}
static int moduleColorFuncColor(lua_State *L) {
static int moduleColorCreate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL.");
if(
@@ -114,7 +114,7 @@ static void moduleColor(lua_State *L) {
}
lua_pop(L, 1);
lua_register(L, "color", moduleColorFuncColor);
lua_register(L, "color", moduleColorCreate);
lua_register(L, "colorRainbow", moduleColorRainbow);
luaL_dostring(L, COLOR_SCRIPT);
@@ -27,8 +27,8 @@ static int moduleSpriteBatchPush(lua_State *L) {
assertNotNull(L, "Lua state is null");
vec2 min, max;
luaVec2Check(L, 1, min);
luaVec2Check(L, 2, max);
moduleVec2Check(L, 1, min);
moduleVec2Check(L, 2, max);
color_t *color = NULL;
if(lua_gettop(L) < 3 || lua_isnil(L, 3)) {
@@ -41,7 +41,7 @@ static int moduleSpriteBatchPush(lua_State *L) {
float_t u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f;
if(lua_gettop(L) >= 4 && !lua_isnil(L, 4)) {
vec4 uv; luaVec4Check(L, 4, uv);
vec4 uv; moduleVec4Check(L, 4, uv);
u0 = uv[0]; v0 = uv[1]; u1 = uv[2]; v1 = uv[3];
}
+2 -2
View File
@@ -14,7 +14,7 @@
static int moduleTextDraw(lua_State *L) {
assertNotNull(L, "Lua state is null");
vec2 pos; luaVec2Check(L, 1, pos);
vec2 pos; moduleVec2Check(L, 1, pos);
if(!lua_isstring(L, 2)) {
return luaL_error(L, "Text to draw must be a string");
@@ -61,7 +61,7 @@ static int moduleTextMeasure(lua_State *L) {
textMeasure(text, &DEFAULT_FONT_TILESET, &w, &h);
vec2 size = { (float_t)w, (float_t)h };
luaVec2Push(L, size);
moduleVec2Push(L, size);
return 1;
}
@@ -49,7 +49,7 @@ static int moduleTilesetTileGetUV(lua_State *l) {
assertNotNull(ts, "Tileset pointer cannot be NULL.");
uint16_t tileIndex = (uint16_t)luaL_checknumber(l, 2);
vec4 uv; tilesetTileGetUV(ts, tileIndex, uv);
luaVec4Push(l, uv);
moduleVec4Push(l, uv);
return 1;
}
@@ -60,7 +60,7 @@ static int moduleTilesetPositionGetUV(lua_State *l) {
uint16_t column = (uint16_t)luaL_checknumber(l, 2);
uint16_t row = (uint16_t)luaL_checknumber(l, 3);
vec4 uv; tilesetPositionGetUV(ts, column, row, uv);
luaVec4Push(l, uv);
moduleVec4Push(l, uv);
return 1;
}
@@ -0,0 +1,110 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/display/entitycamera.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitycam_handle_t;
/**
* __index metamethod for camera component userdata.
*
* @param L Lua state. Arg 1: entitycam userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityCameraIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt");
assertNotNull(h, "invalid entitycam userdata");
const char *k = luaL_checkstring(L, 2);
assertStrLenMin(k, 1, "property key cannot be empty");
if(stringCompare(k, "zNear") == 0) {
lua_pushnumber(L, entityCameraGetZNear(h->entityId, h->compId)); return 1;
} else if(stringCompare(k, "zFar") == 0) {
lua_pushnumber(L, entityCameraGetZFar(h->entityId, h->compId)); return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
}
/**
* __newindex metamethod for camera component userdata.
*
* @param L Lua state. Arg 1: entitycam userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleEntityCameraNewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt");
assertNotNull(h, "invalid entitycam userdata");
const char *k = luaL_checkstring(L, 2);
assertStrLenMin(k, 1, "property key cannot be empty");
if(stringCompare(k, "zNear") == 0) {
entityCameraSetZNear(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
} else if(stringCompare(k, "zFar") == 0) {
entityCameraSetZFar(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
}
luaL_error(L, "entitycam: unknown property '%s'", k);
return 0;
}
/**
* Adds a camera component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitycam userdata).
*/
static int moduleEntityCameraAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA);
entitycam_handle_t *h = lua_newuserdata(L, sizeof(entitycam_handle_t));
assertNotNull(h, "failed to allocate entitycam userdata");
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitycam_mt");
lua_setmetatable(L, -2);
return 1;
}
/**
* Registers the camera component metatable and entityCameraAdd global.
*
* @param L Lua state.
*/
static void moduleEntityCamera(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
luaL_newmetatable(L, "entitycam_mt");
lua_pushcfunction(L, moduleEntityCameraIndex);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityCameraNewIndex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_register(L, "entityCameraAdd", moduleEntityCameraAdd);
}
@@ -0,0 +1,81 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/display/entitymaterial.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitymat_handle_t;
/**
* __index metamethod for material component userdata.
* Delegates all lookups to the metatable (methods only, no plain properties).
*
* @param L Lua state. Arg 1: entitymat userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityMaterialIndex(lua_State *L) {
lua_getmetatable(L, 1);
lua_getfield(L, -1, luaL_checkstring(L, 2));
return 1;
}
/**
* __newindex metamethod for material component userdata.
* Writes color (expects a color_mt userdata). Errors on unknown keys.
*
* @param L Lua state. Arg 1: entitymat userdata. Arg 2: key string. Arg 3: value.
* @return 0.
*/
static int moduleEntityMaterialNewIndex(lua_State *L) {
entitymat_handle_t *h = luaL_checkudata(L, 1, "entitymat_mt");
const char *k = luaL_checkstring(L, 2);
if(stringCompare(k, "color") == 0) {
const color_t *col = (const color_t *)luaL_checkudata(L, 3, "color_mt");
entityMaterialSetColor(h->entityId, h->compId, *col);
return 0;
}
luaL_error(L, "entitymat: unknown property '%s'", k);
return 0;
}
/**
* Adds a material component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitymat userdata).
*/
static int moduleEntityMaterialAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MATERIAL);
entitymat_handle_t *h = lua_newuserdata(L, sizeof(entitymat_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitymat_mt");
lua_setmetatable(L, -2);
return 1;
}
/**
* Registers the material component metatable and entityMaterialAdd global.
*
* @param L Lua state.
*/
static void moduleEntityMaterial(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
luaL_newmetatable(L, "entitymat_mt");
lua_pushcfunction(L, moduleEntityMaterialIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityMaterialNewIndex); lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_register(L, "entityMaterialAdd", moduleEntityMaterialAdd);
}
@@ -0,0 +1,93 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/display/entitymesh.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitymesh_handle_t;
/**
* __index metamethod for mesh component userdata.
* Delegates all lookups to the metatable (methods only, no plain properties).
*
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityMeshIndex(lua_State *L) {
lua_getmetatable(L, 1);
lua_getfield(L, -1, luaL_checkstring(L, 2));
return 1;
}
/**
* Generates an XZ-aligned plane mesh owned by this component.
*
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: width. Arg 3: depth.
* @return 0.
*/
static int moduleEntityMeshGeneratePlane(lua_State *L) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt");
float_t w = (float_t)luaL_checknumber(L, 2);
float_t d = (float_t)luaL_checknumber(L, 3);
errorret_t err = entityMeshGeneratePlane(h->entityId, h->compId, w, d);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generatePlane failed");
return 0;
}
/**
* Generates a Y-axis capsule mesh owned by this component.
*
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: radius. Arg 3: halfHeight.
* @return 0.
*/
static int moduleEntityMeshGenerateCapsule(lua_State *L) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt");
float_t r = (float_t)luaL_checknumber(L, 2);
float_t hh = (float_t)luaL_checknumber(L, 3);
errorret_t err = entityMeshGenerateCapsule(h->entityId, h->compId, r, hh);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generateCapsule failed");
return 0;
}
/**
* Adds a mesh component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitymesh userdata).
*/
static int moduleEntityMeshAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MESH);
entitymesh_handle_t *h = lua_newuserdata(L, sizeof(entitymesh_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitymesh_mt");
lua_setmetatable(L, -2);
return 1;
}
/**
* Registers the mesh component metatable and entityMeshAdd global.
*
* @param L Lua state.
*/
static void moduleEntityMesh(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
luaL_newmetatable(L, "entitymesh_mt");
lua_pushcfunction(L, moduleEntityMeshIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityMeshGeneratePlane); lua_setfield(L, -2, "generatePlane");
lua_pushcfunction(L, moduleEntityMeshGenerateCapsule); lua_setfield(L, -2, "generateCapsule");
lua_pop(L, 1);
lua_register(L, "entityMeshAdd", moduleEntityMeshAdd);
}
@@ -0,0 +1,218 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/physics/entityphysics.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entityphysics_handle_t;
/**
* __index metamethod for physics component userdata.
* Reads velX, velY, velZ, onGround (bool), bodyType; falls through to the
* metatable for method lookups.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityPhysicsIndex(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v;
if(stringCompare(k, "velX") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "velY") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "velZ") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "onGround") == 0) {
lua_pushboolean(L, entityPhysicsIsOnGround(h->entityId, h->compId));
return 1;
} else if(stringCompare(k, "bodyType") == 0) {
lua_pushinteger(L, (lua_Integer)entityPhysicsGetBodyType(h->entityId, h->compId));
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
}
/**
* __newindex metamethod for physics component userdata.
* Writes velX, velY, velZ, bodyType. onGround is read-only. Errors on unknown keys.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: key string. Arg 3: value.
* @return 0.
*/
static int moduleEntityPhysicsNewIndex(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v;
if(stringCompare(k, "velX") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "velY") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "velZ") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "bodyType") == 0) {
entityPhysicsSetBodyType(
h->entityId, h->compId,
(physicsbodytype_t)luaL_checkinteger(L, 3)
);
return 0;
} else if(stringCompare(k, "onGround") == 0) {
luaL_error(L, "entityphysics: onGround is read-only");
return 0;
}
luaL_error(L, "entityphysics: unknown property '%s'", k);
return 0;
}
/**
* Applies an immediate velocity impulse to the physics body.
*
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: impulse x, y, z.
* @return 0.
*/
static int moduleEntityPhysicsApplyImpulse(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
vec3 impulse = {
(float_t)luaL_checknumber(L, 2),
(float_t)luaL_checknumber(L, 3),
(float_t)luaL_checknumber(L, 4)
};
entityPhysicsApplyImpulse(h->entityId, h->compId, impulse);
return 0;
}
/**
* Sets the physics shape to an axis-aligned box.
*
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: half-extents x, y, z.
* @return 0.
*/
static int moduleEntityPhysicsSetShapeCube(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_CUBE;
shape.data.cube.halfExtents[0] = (float_t)luaL_checknumber(L, 2);
shape.data.cube.halfExtents[1] = (float_t)luaL_checknumber(L, 3);
shape.data.cube.halfExtents[2] = (float_t)luaL_checknumber(L, 4);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
}
/**
* Sets the physics shape to a sphere.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: radius.
* @return 0.
*/
static int moduleEntityPhysicsSetShapeSphere(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_SPHERE;
shape.data.sphere.radius = (float_t)luaL_checknumber(L, 2);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
}
/**
* Sets the physics shape to a Y-axis capsule.
*
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: radius. Arg 3: halfHeight.
* @return 0.
*/
static int moduleEntityPhysicsSetShapeCapsule(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_CAPSULE;
shape.data.capsule.radius = (float_t)luaL_checknumber(L, 2);
shape.data.capsule.halfHeight = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
}
/**
* Sets the physics shape to an infinite plane.
*
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: normal x, y, z.
* Arg 5: distance from origin.
* @return 0.
*/
static int moduleEntityPhysicsSetShapePlane(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
physicsshape_t shape;
shape.type = PHYSICS_SHAPE_PLANE;
shape.data.plane.normal[0] = (float_t)luaL_checknumber(L, 2);
shape.data.plane.normal[1] = (float_t)luaL_checknumber(L, 3);
shape.data.plane.normal[2] = (float_t)luaL_checknumber(L, 4);
shape.data.plane.distance = (float_t)luaL_checknumber(L, 5);
entityPhysicsSetShape(h->entityId, h->compId, shape);
return 0;
}
/**
* Adds a physics component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entityphysics userdata).
*/
static int moduleEntityPhysicsAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_PHYSICS);
entityphysics_handle_t *h = lua_newuserdata(L, sizeof(entityphysics_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entityphysics_mt");
lua_setmetatable(L, -2);
return 1;
}
/**
* Registers the physics component metatable, entityPhysicsAdd global, and
* PHYSICS_BODY_* / PHYSICS_SHAPE_* integer constants.
*
* @param L Lua state.
*/
static void moduleEntityPhysics(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
luaL_newmetatable(L, "entityphysics_mt");
lua_pushcfunction(L, moduleEntityPhysicsIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityPhysicsNewIndex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleEntityPhysicsApplyImpulse); lua_setfield(L, -2, "applyImpulse");
lua_pushcfunction(L, moduleEntityPhysicsSetShapeCube); lua_setfield(L, -2, "setShapeCube");
lua_pushcfunction(L, moduleEntityPhysicsSetShapeSphere); lua_setfield(L, -2, "setShapeSphere");
lua_pushcfunction(L, moduleEntityPhysicsSetShapeCapsule); lua_setfield(L, -2, "setShapeCapsule");
lua_pushcfunction(L, moduleEntityPhysicsSetShapePlane); lua_setfield(L, -2, "setShapePlane");
lua_pop(L, 1);
lua_register(L, "entityPhysicsAdd", moduleEntityPhysicsAdd);
lua_pushinteger(L, PHYSICS_BODY_STATIC); lua_setglobal(L, "PHYSICS_BODY_STATIC");
lua_pushinteger(L, PHYSICS_BODY_DYNAMIC); lua_setglobal(L, "PHYSICS_BODY_DYNAMIC");
lua_pushinteger(L, PHYSICS_BODY_KINEMATIC); lua_setglobal(L, "PHYSICS_BODY_KINEMATIC");
lua_pushinteger(L, PHYSICS_SHAPE_CUBE); lua_setglobal(L, "PHYSICS_SHAPE_CUBE");
lua_pushinteger(L, PHYSICS_SHAPE_SPHERE); lua_setglobal(L, "PHYSICS_SHAPE_SPHERE");
lua_pushinteger(L, PHYSICS_SHAPE_CAPSULE); lua_setglobal(L, "PHYSICS_SHAPE_CAPSULE");
lua_pushinteger(L, PHYSICS_SHAPE_PLANE); lua_setglobal(L, "PHYSICS_SHAPE_PLANE");
}
@@ -0,0 +1,175 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/component/display/entityposition.h"
typedef struct {
entityid_t entityId;
componentid_t compId;
} entitypos_handle_t;
/**
* __index metamethod for position component userdata.
* Reads x, y, z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ; falls through to
* the metatable for method lookups.
*
* @param L Lua state. Arg 1: entitypos userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityPositionIndex(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v;
if(stringCompare(k, "x") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "z") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "rotY") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
}
/**
* __newindex metamethod for position component userdata.
* Writes x, y, z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ and rebuilds the
* transform. Errors on unknown keys.
*
* @param L Lua state. Arg 1: entitypos userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleEntityPositionNewIndex(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v;
if(stringCompare(k, "x") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "z") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotY") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
}
luaL_error(L, "entitypos: unknown property '%s'", k);
return 0;
}
/**
* Rotates the entity to face a world-space target point.
* An optional up vector may be provided (args 5-7); defaults to (0,1,0).
*
* @param L Lua state. Arg 1: entitypos userdata. Args 2-4: target x, y, z.
* Args 5-7 (optional): up x, y, z.
* @return 0.
*/
static int moduleEntityPositionLookAt(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
vec3 target = {
(float_t)luaL_checknumber(L, 2),
(float_t)luaL_checknumber(L, 3),
(float_t)luaL_checknumber(L, 4)
};
vec3 up = { 0.0f, 1.0f, 0.0f };
if(lua_gettop(L) >= 7) {
up[0] = (float_t)luaL_checknumber(L, 5);
up[1] = (float_t)luaL_checknumber(L, 6);
up[2] = (float_t)luaL_checknumber(L, 7);
}
vec3 eye;
entityPositionGetPosition(h->entityId, h->compId, eye);
entityPositionLookAt(h->entityId, h->compId, target, up, eye);
return 0;
}
/**
* Adds a position component to an entity and returns its userdata handle.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitypos userdata).
*/
static int moduleEntityPositionAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_POSITION);
entitypos_handle_t *h = lua_newuserdata(L, sizeof(entitypos_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitypos_mt");
lua_setmetatable(L, -2);
return 1;
}
/**
* Registers the position component metatable and entityPositionAdd global.
*
* @param L Lua state.
*/
static void moduleEntityPosition(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
luaL_newmetatable(L, "entitypos_mt");
lua_pushcfunction(L, moduleEntityPositionIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityPositionNewIndex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleEntityPositionLookAt); lua_setfield(L, -2, "lookAt");
lua_pop(L, 1);
lua_register(L, "entityPositionAdd", moduleEntityPositionAdd);
}
+33 -274
View File
@@ -9,239 +9,11 @@
#include "script/module/modulebase.h"
#include "entity/entity.h"
#include "entity/entitymanager.h"
#include "entity/component/display/entityposition.h"
#include "entity/component/display/entitycamera.h"
#include "entity/component/display/entitymesh.h"
#include "entity/component/display/entitymaterial.h"
// ============================================================================
// Handles
// ============================================================================
typedef struct { entityid_t entityId; componentid_t compId; } entitypos_handle_t;
typedef struct { entityid_t entityId; componentid_t compId; } entitycam_handle_t;
typedef struct { entityid_t entityId; componentid_t compId; } entitymesh_handle_t;
typedef struct { entityid_t entityId; componentid_t compId; } entitymat_handle_t;
// ============================================================================
// entityPosition
// ============================================================================
static int _posIndex(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v;
if(stringCompare(k, "x") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "z") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "rotY") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
}
static int _posNewindex(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v;
if(stringCompare(k, "x") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "z") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotY") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
}
luaL_error(L, "entitypos: unknown property '%s'", k);
return 0;
}
static int _posLookAt(lua_State *L) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt");
vec3 target = {
(float_t)luaL_checknumber(L, 2),
(float_t)luaL_checknumber(L, 3),
(float_t)luaL_checknumber(L, 4)
};
vec3 up = { 0.0f, 1.0f, 0.0f };
if(lua_gettop(L) >= 7) {
up[0] = (float_t)luaL_checknumber(L, 5);
up[1] = (float_t)luaL_checknumber(L, 6);
up[2] = (float_t)luaL_checknumber(L, 7);
}
vec3 eye;
entityPositionGetPosition(h->entityId, h->compId, eye);
entityPositionLookAt(h->entityId, h->compId, target, up, eye);
return 0;
}
static int _entityPositionAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_POSITION);
entitypos_handle_t *h = lua_newuserdata(L, sizeof(entitypos_handle_t));
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitypos_mt");
lua_setmetatable(L, -2);
return 1;
}
// ============================================================================
// entityCamera
// ============================================================================
static int _camIndex(lua_State *L) {
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt");
const char *k = luaL_checkstring(L, 2);
if(stringCompare(k, "zNear") == 0) {
lua_pushnumber(L, entityCameraGetZNear(h->entityId, h->compId)); return 1;
} else if(stringCompare(k, "zFar") == 0) {
lua_pushnumber(L, entityCameraGetZFar(h->entityId, h->compId)); return 1;
}
lua_getmetatable(L, 1); lua_getfield(L, -1, k); return 1;
}
static int _camNewindex(lua_State *L) {
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt");
const char *k = luaL_checkstring(L, 2);
if(stringCompare(k, "zNear") == 0) {
entityCameraSetZNear(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
} else if(stringCompare(k, "zFar") == 0) {
entityCameraSetZFar(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
}
luaL_error(L, "entitycam: unknown property '%s'", k); return 0;
}
static int _entityCameraAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA);
entitycam_handle_t *h = lua_newuserdata(L, sizeof(entitycam_handle_t));
h->entityId = id; h->compId = comp;
luaL_getmetatable(L, "entitycam_mt"); lua_setmetatable(L, -2);
return 1;
}
// ============================================================================
// entityMesh
// ============================================================================
static int _meshIndex(lua_State *L) {
lua_getmetatable(L, 1); lua_getfield(L, -1, luaL_checkstring(L, 2)); return 1;
}
static int _meshGeneratePlane(lua_State *L) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt");
float_t w = (float_t)luaL_checknumber(L, 2);
float_t d = (float_t)luaL_checknumber(L, 3);
errorret_t err = entityMeshGeneratePlane(h->entityId, h->compId, w, d);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generatePlane failed");
return 0;
}
static int _meshGenerateCapsule(lua_State *L) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt");
float_t r = (float_t)luaL_checknumber(L, 2);
float_t hh = (float_t)luaL_checknumber(L, 3);
errorret_t err = entityMeshGenerateCapsule(h->entityId, h->compId, r, hh);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generateCapsule failed");
return 0;
}
static int _entityMeshAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MESH);
entitymesh_handle_t *h = lua_newuserdata(L, sizeof(entitymesh_handle_t));
h->entityId = id; h->compId = comp;
luaL_getmetatable(L, "entitymesh_mt"); lua_setmetatable(L, -2);
return 1;
}
// ============================================================================
// entityMaterial
// ============================================================================
static int _matIndex(lua_State *L) {
lua_getmetatable(L, 1); lua_getfield(L, -1, luaL_checkstring(L, 2)); return 1;
}
static int _matNewindex(lua_State *L) {
entitymat_handle_t *h = luaL_checkudata(L, 1, "entitymat_mt");
const char *k = luaL_checkstring(L, 2);
if(stringCompare(k, "color") == 0) {
const color_t *col = (const color_t *)luaL_checkudata(L, 3, "color_mt");
entityMaterialSetColor(h->entityId, h->compId, *col);
return 0;
}
luaL_error(L, "entitymat: unknown property '%s'", k); return 0;
}
static int _entityMaterialAdd(lua_State *L) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MATERIAL);
entitymat_handle_t *h = lua_newuserdata(L, sizeof(entitymat_handle_t));
h->entityId = id; h->compId = comp;
luaL_getmetatable(L, "entitymat_mt"); lua_setmetatable(L, -2);
return 1;
}
// ============================================================================
// Component type Lua constants (auto-generated from componentlist.h)
// ============================================================================
#include "component/moduleentityposition.h"
#include "component/moduleentitycamera.h"
#include "component/moduleentitymesh.h"
#include "component/moduleentitymaterial.h"
#include "component/moduleentityphysics.h"
#define X(enumName, type, field, init, dispose) \
"COMPONENT_TYPE_" #enumName " = \"" #field "\"\n"
@@ -250,10 +22,6 @@ static const char_t *COMPONENT_TYPE_SCRIPT =
;
#undef X
// ============================================================================
// Entity base class Lua script
// ============================================================================
static const char_t *ENTITY_SCRIPT =
"Entity = {}\n"
"Entity.__index = Entity\n"
@@ -269,6 +37,7 @@ static const char_t *ENTITY_SCRIPT =
" [COMPONENT_TYPE_CAMERA] = entityCameraAdd,\n"
" [COMPONENT_TYPE_MESH] = entityMeshAdd,\n"
" [COMPONENT_TYPE_MATERIAL] = entityMaterialAdd,\n"
" [COMPONENT_TYPE_PHYSICS] = entityPhysicsAdd,\n"
"}\n"
"\n"
"function Entity.new()\n"
@@ -287,55 +56,45 @@ static const char_t *ENTITY_SCRIPT =
"end\n"
;
// ============================================================================
// entityAdd / entityRemove
// ============================================================================
static int _entityAdd(lua_State *L) {
/**
* Allocates a new entity and pushes its id onto the Lua stack.
*
* @param L Lua state.
* @return 1 (entity id number).
*/
static int moduleEntityAdd(lua_State *L) {
lua_pushnumber(L, (lua_Number)entityManagerAdd());
return 1;
}
static int _entityRemove(lua_State *L) {
/**
* Disposes the entity with the given id.
*
* @param L Lua state. Arg 1: entity id (number).
* @return 0.
*/
static int moduleEntityRemove(lua_State *L) {
entityDispose((entityid_t)luaL_checknumber(L, 1));
return 0;
}
// ============================================================================
// Register
// ============================================================================
/**
* Registers all entity and component modules, component type constants, and
* the Entity base class into the Lua state.
*
* @param L Lua state.
*/
static void moduleEntity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
luaL_newmetatable(L, "entitypos_mt");
lua_pushcfunction(L, _posIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, _posNewindex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, _posLookAt); lua_setfield(L, -2, "lookAt");
lua_pop(L, 1);
moduleEntityPosition(L);
moduleEntityCamera(L);
moduleEntityMesh(L);
moduleEntityMaterial(L);
moduleEntityPhysics(L);
luaL_newmetatable(L, "entitycam_mt");
lua_pushcfunction(L, _camIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, _camNewindex); lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
luaL_newmetatable(L, "entitymesh_mt");
lua_pushcfunction(L, _meshIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, _meshGeneratePlane); lua_setfield(L, -2, "generatePlane");
lua_pushcfunction(L, _meshGenerateCapsule); lua_setfield(L, -2, "generateCapsule");
lua_pop(L, 1);
luaL_newmetatable(L, "entitymat_mt");
lua_pushcfunction(L, _matIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, _matNewindex); lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_register(L, "entityAdd", _entityAdd);
lua_register(L, "entityRemove", _entityRemove);
lua_register(L, "entityPositionAdd", _entityPositionAdd);
lua_register(L, "entityCameraAdd", _entityCameraAdd);
lua_register(L, "entityMeshAdd", _entityMeshAdd);
lua_register(L, "entityMaterialAdd", _entityMaterialAdd);
lua_register(L, "entityAdd", moduleEntityAdd);
lua_register(L, "entityRemove", moduleEntityRemove);
luaL_dostring(L, COMPONENT_TYPE_SCRIPT);
luaL_dostring(L, ENTITY_SCRIPT);
+234 -52
View File
@@ -10,108 +10,290 @@
#include "modulevec3.h"
#include "modulevec4.h"
static void luaMat4Push(lua_State *L, mat4 m) {
/**
* Pushes a new mat4 userdata onto the Lua stack with the mat4_mt metatable.
*
* @param L Lua state.
* @param m Source matrix to copy.
*/
static void moduleMat4Push(lua_State *L, mat4 m) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *u = (mat4 *)lua_newuserdata(L, sizeof(mat4));
glm_mat4_copy(m, *u);
luaL_getmetatable(L, "mat4_mt");
lua_setmetatable(L, -2);
}
static void luaMat4Check(lua_State *L, int idx, mat4 out) {
/**
* Reads a mat4 userdata from the given stack index into out.
*
* @param L Lua state.
* @param idx Stack index of the mat4 userdata.
* @param out Destination matrix.
*/
static void moduleMat4Check(lua_State *L, int idx, mat4 out) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, idx, "mat4_mt");
glm_mat4_copy(*m, out);
}
static int mat4Index(lua_State *L) {
/**
* __index metamethod for mat4 userdata.
* Delegates all lookups to the metatable (methods only).
*
* @param L Lua state. Arg 1: mat4 userdata. Arg 2: key string.
* @return 1.
*/
static int moduleMat4Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_getmetatable(L, 1);
lua_getfield(L, -1, luaL_checkstring(L, 2));
lua_remove(L, -2);
return 1;
}
static int mat4OpMul(lua_State *L) {
/**
* __mul metamethod: returns a * b as a new mat4.
*
* @param L Lua state. Arg 1: mat4. Arg 2: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *a = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(a, "invalid mat4 userdata");
mat4 *b = (mat4 *)luaL_checkudata(L, 2, "mat4_mt");
mat4 r; glm_mat4_mul(*a, *b, r);
luaMat4Push(L, r); return 1;
assertNotNull(b, "invalid mat4 userdata");
mat4 r;
glm_mat4_mul(*a, *b, r);
moduleMat4Push(L, r);
return 1;
}
static int mat4OpToString(lua_State *L) {
lua_pushstring(L, "mat4(...)"); return 1;
/**
* __tostring metamethod: returns a placeholder string.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (string).
*/
static int moduleMat4OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_pushstring(L, "mat4(...)");
return 1;
}
static int mat4Transpose(lua_State *L) {
/**
* Returns the transpose of a mat4 as a new mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4Transpose(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
mat4 r; glm_mat4_transpose_to(*m, r);
luaMat4Push(L, r); return 1;
assertNotNull(m, "invalid mat4 userdata");
mat4 r;
glm_mat4_transpose_to(*m, r);
moduleMat4Push(L, r);
return 1;
}
static int mat4Inverse(lua_State *L) {
/**
* Returns the inverse of a mat4 as a new mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4Inverse(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
mat4 r; glm_mat4_inv(*m, r);
luaMat4Push(L, r); return 1;
assertNotNull(m, "invalid mat4 userdata");
mat4 r;
glm_mat4_inv(*m, r);
moduleMat4Push(L, r);
return 1;
}
static int mat4MulVec3(lua_State *L) {
/**
* Multiplies a mat4 by a vec3, with an optional w component (default 1.0).
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3. Arg 3 (optional): number w.
* @return 1 (new vec3).
*/
static int moduleMat4MulVec3(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
vec3 v; luaVec3Check(L, 2, v);
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
float_t w = lua_gettop(L) >= 3 ? (float_t)luaL_checknumber(L, 3) : 1.0f;
vec3 r; glm_mat4_mulv3(*m, v, w, r);
luaVec3Push(L, r); return 1;
vec3 r;
glm_mat4_mulv3(*m, v, w, r);
moduleVec3Push(L, r);
return 1;
}
static int mat4MulVec4(lua_State *L) {
/**
* Multiplies a mat4 by a vec4.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleMat4MulVec4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
vec4 v; luaVec4Check(L, 2, v);
vec4 r; glm_mat4_mulv(*m, v, r);
luaVec4Push(L, r); return 1;
assertNotNull(m, "invalid mat4 userdata");
vec4 v;
moduleVec4Check(L, 2, v);
vec4 r;
glm_mat4_mulv(*m, v, r);
moduleVec4Push(L, r);
return 1;
}
static int mat4Translate(lua_State *L) {
/**
* Returns a copy of a mat4 translated by a vec3.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3.
* @return 1 (new mat4).
*/
static int moduleMat4Translate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
vec3 v; luaVec3Check(L, 2, v);
mat4 r; glm_mat4_copy(*m, r);
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
mat4 r;
glm_mat4_copy(*m, r);
glm_translate(r, v);
luaMat4Push(L, r); return 1;
moduleMat4Push(L, r);
return 1;
}
static int mat4Scale(lua_State *L) {
/**
* Returns a copy of a mat4 scaled by a vec3.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3.
* @return 1 (new mat4).
*/
static int moduleMat4Scale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
vec3 v; luaVec3Check(L, 2, v);
mat4 r; glm_mat4_copy(*m, r);
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
mat4 r;
glm_mat4_copy(*m, r);
glm_scale(r, v);
luaMat4Push(L, r); return 1;
moduleMat4Push(L, r);
return 1;
}
static int mat4Identity(lua_State *L) {
mat4 r; glm_mat4_identity(r);
luaMat4Push(L, r); return 1;
/**
* Returns a new identity mat4.
*
* @param L Lua state.
* @return 1 (new mat4).
*/
static int moduleMat4Identity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 r;
glm_mat4_identity(r);
moduleMat4Push(L, r);
return 1;
}
static int mat4Determinant(lua_State *L) {
/**
* Returns the determinant of a mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (number).
*/
static int moduleMat4Determinant(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
lua_pushnumber(L, (lua_Number)glm_mat4_det(*m)); return 1;
assertNotNull(m, "invalid mat4 userdata");
lua_pushnumber(L, (lua_Number)glm_mat4_det(*m));
return 1;
}
static int mat4Create(lua_State *L) {
mat4 m; glm_mat4_identity(m);
luaMat4Push(L, m); return 1;
/**
* Constructor: creates a new identity mat4.
*
* @param L Lua state.
* @return 1 (new mat4).
*/
static int moduleMat4Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 m;
glm_mat4_identity(m);
moduleMat4Push(L, m);
return 1;
}
static void moduleMathMat4(lua_State *L) {
if(!luaL_newmetatable(L, "mat4_mt")) { lua_pop(L, 1); return; }
/**
* Registers the mat4 metatable and mat4 constructor global.
*
* @param L Lua state.
*/
static void moduleMat4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_pushcfunction(L, mat4Index); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, mat4OpMul); lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, mat4OpToString); lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, mat4Transpose); lua_setfield(L, -2, "transpose");
lua_pushcfunction(L, mat4Inverse); lua_setfield(L, -2, "inverse");
lua_pushcfunction(L, mat4MulVec3); lua_setfield(L, -2, "mulVec3");
lua_pushcfunction(L, mat4MulVec4); lua_setfield(L, -2, "mulVec4");
lua_pushcfunction(L, mat4Translate); lua_setfield(L, -2, "translate");
lua_pushcfunction(L, mat4Scale); lua_setfield(L, -2, "scale");
lua_pushcfunction(L, mat4Identity); lua_setfield(L, -2, "identity");
lua_pushcfunction(L, mat4Determinant); lua_setfield(L, -2, "determinant");
if(!luaL_newmetatable(L, "mat4_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleMat4Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleMat4OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleMat4OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleMat4Transpose);
lua_setfield(L, -2, "transpose");
lua_pushcfunction(L, moduleMat4Inverse);
lua_setfield(L, -2, "inverse");
lua_pushcfunction(L, moduleMat4MulVec3);
lua_setfield(L, -2, "mulVec3");
lua_pushcfunction(L, moduleMat4MulVec4);
lua_setfield(L, -2, "mulVec4");
lua_pushcfunction(L, moduleMat4Translate);
lua_setfield(L, -2, "translate");
lua_pushcfunction(L, moduleMat4Scale);
lua_setfield(L, -2, "scale");
lua_pushcfunction(L, moduleMat4Identity);
lua_setfield(L, -2, "identity");
lua_pushcfunction(L, moduleMat4Determinant);
lua_setfield(L, -2, "determinant");
lua_pop(L, 1);
lua_register(L, "mat4", mat4Create);
lua_register(L, "mat4", moduleMat4Create);
}
+10 -4
View File
@@ -12,10 +12,16 @@
#include "modulevec4.h"
#include "modulemat4.h"
/**
* Registers all math modules: vec2, vec3, vec4, mat4.
*
* @param L Lua state.
*/
static void moduleMath(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
moduleMathVec2(L);
moduleMathVec3(L);
moduleMathVec4(L);
moduleMathMat4(L);
moduleVec2(L);
moduleVec3(L);
moduleVec4(L);
moduleMat4(L);
}
+341 -63
View File
@@ -8,162 +8,440 @@
#pragma once
#include "script/module/modulebase.h"
static void luaVec2Push(lua_State *L, vec2 v) {
/**
* Pushes a new vec2 userdata onto the Lua stack with the vec2_mt metatable.
*
* @param L Lua state.
* @param v Source vector to copy.
*/
static void moduleVec2Push(lua_State *L, vec2 v) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *u = (vec2 *)lua_newuserdata(L, sizeof(vec2));
glm_vec2_copy(v, *u);
luaL_getmetatable(L, "vec2_mt");
lua_setmetatable(L, -2);
}
static void luaVec2Check(lua_State *L, int idx, vec2 out) {
/**
* Reads a vec2 userdata from the given stack index into out.
*
* @param L Lua state.
* @param idx Stack index of the vec2 userdata.
* @param out Destination vector.
*/
static void moduleVec2Check(lua_State *L, int idx, vec2 out) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, idx, "vec2_mt");
glm_vec2_copy(*v, out);
}
static int vec2Index(lua_State *L) {
/**
* __index metamethod for vec2 userdata.
* Supports integer indices 1-2 and string keys x, y; falls through to the
* metatable for method lookups.
*
* @param L Lua state. Arg 1: vec2 userdata. Arg 2: integer or string key.
* @return 1.
*/
static int moduleVec2Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
const char *key = luaL_checkstring(L, 2);
if(stringCompare(key, "x") == 0) { lua_pushnumber(L, (lua_Number)(*v)[0]); return 1; }
if(stringCompare(key, "y") == 0) { lua_pushnumber(L, (lua_Number)(*v)[1]); return 1; }
assertStrLenMin(key, 1, "property key cannot be empty");
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 2) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringCompare(key, "x") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringCompare(key, "y") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
}
static int vec2Newindex(lua_State *L) {
/**
* __newindex metamethod for vec2 userdata.
* Writes x, y. Errors on unknown keys.
*
* @param L Lua state. Arg 1: vec2 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec2NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
const char *key = luaL_checkstring(L, 2);
if(stringCompare(key, "x") == 0) { (*v)[0] = (float_t)luaL_checknumber(L, 3); return 0; }
if(stringCompare(key, "y") == 0) { (*v)[1] = (float_t)luaL_checknumber(L, 3); return 0; }
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringCompare(key, "x") == 0) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "y") == 0) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec2: unknown property '%s'", key);
return 0;
}
static int vec2OpAdd(lua_State *L) {
/**
* __add metamethod: returns a + b as a new vec2.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
vec2 r; glm_vec2_add(*a, *b, r);
luaVec2Push(L, r); return 1;
assertNotNull(b, "invalid vec2 userdata");
vec2 r;
glm_vec2_add(*a, *b, r);
moduleVec2Push(L, r);
return 1;
}
static int vec2OpSub(lua_State *L) {
/**
* __sub metamethod: returns a - b as a new vec2.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
vec2 r; glm_vec2_sub(*a, *b, r);
luaVec2Push(L, r); return 1;
assertNotNull(b, "invalid vec2 userdata");
vec2 r;
glm_vec2_sub(*a, *b, r);
moduleVec2Push(L, r);
return 1;
}
static int vec2OpMul(lua_State *L) {
/**
* __mul metamethod: returns v * scalar or scalar * v as a new vec2.
*
* @param L Lua state. Arg 1: vec2 or number. Arg 2: number or vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec2 *v = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
glm_vec2_scale(*v, s, r);
} else {
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec2_scale(*v, s, r);
}
luaVec2Push(L, r); return 1;
moduleVec2Push(L, r);
return 1;
}
static int vec2OpDiv(lua_State *L) {
/**
* __div metamethod: returns v / scalar as a new vec2.
*
* @param L Lua state. Arg 1: vec2. Arg 2: number.
* @return 1 (new vec2).
*/
static int moduleVec2OpDiv(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
vec2 r; glm_vec2_divs(*v, s, r);
luaVec2Push(L, r); return 1;
vec2 r;
glm_vec2_divs(*v, s, r);
moduleVec2Push(L, r);
return 1;
}
static int vec2OpUnm(lua_State *L) {
/**
* __unm metamethod: returns -v as a new vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2OpUnm(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
vec2 r; glm_vec2_negate_to(*v, r);
luaVec2Push(L, r); return 1;
assertNotNull(v, "invalid vec2 userdata");
vec2 r;
glm_vec2_negate_to(*v, r);
moduleVec2Push(L, r);
return 1;
}
static int vec2OpEq(lua_State *L) {
/**
* __eq metamethod: component-wise equality test.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (boolean).
*/
static int moduleVec2OpEq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1]);
return 1;
}
static int vec2OpToString(lua_State *L) {
/**
* __tostring metamethod: returns a human-readable representation.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (string).
*/
static int moduleVec2OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
char buf[64];
snprintf(buf, sizeof(buf), "vec2(%.3f, %.3f)", (*v)[0], (*v)[1]);
lua_pushstring(L, buf); return 1;
lua_pushstring(L, buf);
return 1;
}
static int vec2Dot(lua_State *L) {
/**
* Returns the dot product of two vec2 values.
*
* @param L Lua state. Arg 1: vec2. Arg 2: vec2.
* @return 1 (number).
*/
static int moduleVec2Dot(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
lua_pushnumber(L, (lua_Number)glm_vec2_dot(*a, *b)); return 1;
assertNotNull(b, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_dot(*a, *b));
return 1;
}
static int vec2Length(lua_State *L) {
/**
* Returns the length (magnitude) of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (number).
*/
static int moduleVec2Length(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
lua_pushnumber(L, (lua_Number)glm_vec2_norm(*v)); return 1;
assertNotNull(v, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_norm(*v));
return 1;
}
static int vec2LengthSq(lua_State *L) {
/**
* Returns the squared length of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (number).
*/
static int moduleVec2LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
lua_pushnumber(L, (lua_Number)glm_vec2_norm2(*v)); return 1;
assertNotNull(v, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_norm2(*v));
return 1;
}
static int vec2Normalize(lua_State *L) {
/**
* Returns a normalized copy of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
vec2 r; glm_vec2_normalize_to(*v, r);
luaVec2Push(L, r); return 1;
assertNotNull(v, "invalid vec2 userdata");
vec2 r;
glm_vec2_normalize_to(*v, r);
moduleVec2Push(L, r);
return 1;
}
static int vec2Lerp(lua_State *L) {
/**
* Linearly interpolates between two vec2 values.
*
* @param L Lua state. Arg 1: vec2 a. Arg 2: vec2 b. Arg 3: number t.
* @return 1 (new vec2).
*/
static int moduleVec2Lerp(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec2 r; glm_vec2_lerp(*a, *b, t, r);
luaVec2Push(L, r); return 1;
vec2 r;
glm_vec2_lerp(*a, *b, t, r);
moduleVec2Push(L, r);
return 1;
}
static int vec2Distance(lua_State *L) {
/**
* Returns the distance between two vec2 points.
*
* @param L Lua state. Arg 1: vec2 a. Arg 2: vec2 b.
* @return 1 (number).
*/
static int moduleVec2Distance(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
lua_pushnumber(L, (lua_Number)glm_vec2_distance(*a, *b)); return 1;
assertNotNull(b, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_distance(*a, *b));
return 1;
}
static int vec2Negate(lua_State *L) {
/**
* Returns a negated copy of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
vec2 r; glm_vec2_negate_to(*v, r);
luaVec2Push(L, r); return 1;
assertNotNull(v, "invalid vec2 userdata");
vec2 r;
glm_vec2_negate_to(*v, r);
moduleVec2Push(L, r);
return 1;
}
static int vec2Create(lua_State *L) {
/**
* Constructor: creates a vec2 from two optional numbers (defaults to 0).
*
* @param L Lua state. Arg 1 (optional): x. Arg 2 (optional): y.
* @return 1 (new vec2).
*/
static int moduleVec2Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 v = {0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
luaVec2Push(L, v); return 1;
moduleVec2Push(L, v);
return 1;
}
static void moduleMathVec2(lua_State *L) {
if(!luaL_newmetatable(L, "vec2_mt")) { lua_pop(L, 1); return; }
/**
* Registers the vec2 metatable and vec2 constructor global.
*
* @param L Lua state.
*/
static void moduleVec2(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_pushcfunction(L, vec2Index); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, vec2Newindex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, vec2OpAdd); lua_setfield(L, -2, "__add");
lua_pushcfunction(L, vec2OpSub); lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, vec2OpMul); lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, vec2OpDiv); lua_setfield(L, -2, "__div");
lua_pushcfunction(L, vec2OpUnm); lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, vec2OpEq); lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, vec2OpToString); lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, vec2Dot); lua_setfield(L, -2, "dot");
lua_pushcfunction(L, vec2Length); lua_setfield(L, -2, "length");
lua_pushcfunction(L, vec2LengthSq); lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, vec2Normalize); lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, vec2Lerp); lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, vec2Distance); lua_setfield(L, -2, "distance");
lua_pushcfunction(L, vec2Negate); lua_setfield(L, -2, "negate");
if(!luaL_newmetatable(L, "vec2_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec2Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec2NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec2OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec2OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec2OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec2OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec2OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec2OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec2OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec2Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec2Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec2LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec2Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec2Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec2Distance);
lua_setfield(L, -2, "distance");
lua_pushcfunction(L, moduleVec2Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec2", vec2Create);
lua_register(L, "vec2", moduleVec2Create);
}
+370 -69
View File
@@ -8,173 +8,474 @@
#pragma once
#include "script/module/modulebase.h"
static void luaVec3Push(lua_State *L, vec3 v) {
/**
* Pushes a new vec3 userdata onto the Lua stack with the vec3_mt metatable.
*
* @param L Lua state.
* @param v Source vector to copy.
*/
static void moduleVec3Push(lua_State *L, vec3 v) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *u = (vec3 *)lua_newuserdata(L, sizeof(vec3));
glm_vec3_copy(v, *u);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
}
static void luaVec3Check(lua_State *L, int idx, vec3 out) {
/**
* Reads a vec3 userdata from the given stack index into out.
*
* @param L Lua state.
* @param idx Stack index of the vec3 userdata.
* @param out Destination vector.
*/
static void moduleVec3Check(lua_State *L, int idx, vec3 out) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, idx, "vec3_mt");
glm_vec3_copy(*v, out);
}
static int vec3Index(lua_State *L) {
/**
* __index metamethod for vec3 userdata.
* Supports integer indices 1-3 and string keys x, y, z; falls through to the
* metatable for method lookups.
*
* @param L Lua state. Arg 1: vec3 userdata. Arg 2: integer or string key.
* @return 1.
*/
static int moduleVec3Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
const char *key = luaL_checkstring(L, 2);
if(stringCompare(key, "x") == 0) { lua_pushnumber(L, (lua_Number)(*v)[0]); return 1; }
if(stringCompare(key, "y") == 0) { lua_pushnumber(L, (lua_Number)(*v)[1]); return 1; }
if(stringCompare(key, "z") == 0) { lua_pushnumber(L, (lua_Number)(*v)[2]); return 1; }
assertStrLenMin(key, 1, "property key cannot be empty");
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 3) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringCompare(key, "x") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringCompare(key, "y") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
if(stringCompare(key, "z") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[2]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
}
static int vec3Newindex(lua_State *L) {
/**
* __newindex metamethod for vec3 userdata.
* Writes x, y, z. Errors on unknown keys.
*
* @param L Lua state. Arg 1: vec3 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec3NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
const char *key = luaL_checkstring(L, 2);
if(stringCompare(key, "x") == 0) { (*v)[0] = (float_t)luaL_checknumber(L, 3); return 0; }
if(stringCompare(key, "y") == 0) { (*v)[1] = (float_t)luaL_checknumber(L, 3); return 0; }
if(stringCompare(key, "z") == 0) { (*v)[2] = (float_t)luaL_checknumber(L, 3); return 0; }
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringCompare(key, "x") == 0) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "y") == 0) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "z") == 0) {
(*v)[2] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec3: unknown property '%s'", key);
return 0;
}
static int vec3OpAdd(lua_State *L) {
/**
* __add metamethod: returns a + b as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
vec3 r; glm_vec3_add(*a, *b, r);
luaVec3Push(L, r); return 1;
assertNotNull(b, "invalid vec3 userdata");
vec3 r;
glm_vec3_add(*a, *b, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3OpSub(lua_State *L) {
/**
* __sub metamethod: returns a - b as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
vec3 r; glm_vec3_sub(*a, *b, r);
luaVec3Push(L, r); return 1;
assertNotNull(b, "invalid vec3 userdata");
vec3 r;
glm_vec3_sub(*a, *b, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3OpMul(lua_State *L) {
/**
* __mul metamethod: returns v * scalar or scalar * v as a new vec3.
*
* @param L Lua state. Arg 1: vec3 or number. Arg 2: number or vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec3 *v = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
glm_vec3_scale(*v, s, r);
} else {
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec3_scale(*v, s, r);
}
luaVec3Push(L, r); return 1;
moduleVec3Push(L, r);
return 1;
}
static int vec3OpDiv(lua_State *L) {
/**
* __div metamethod: returns v / scalar as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: number.
* @return 1 (new vec3).
*/
static int moduleVec3OpDiv(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
vec3 r; glm_vec3_divs(*v, s, r);
luaVec3Push(L, r); return 1;
vec3 r;
glm_vec3_divs(*v, s, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3OpUnm(lua_State *L) {
/**
* __unm metamethod: returns -v as a new vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3OpUnm(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
vec3 r; glm_vec3_negate_to(*v, r);
luaVec3Push(L, r); return 1;
assertNotNull(v, "invalid vec3 userdata");
vec3 r;
glm_vec3_negate_to(*v, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3OpEq(lua_State *L) {
/**
* __eq metamethod: component-wise equality test.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (boolean).
*/
static int moduleVec3OpEq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] && (*a)[2] == (*b)[2]);
return 1;
}
static int vec3OpToString(lua_State *L) {
/**
* __tostring metamethod: returns a human-readable representation.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (string).
*/
static int moduleVec3OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
char buf[80];
snprintf(buf, sizeof(buf), "vec3(%.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2]);
lua_pushstring(L, buf); return 1;
lua_pushstring(L, buf);
return 1;
}
static int vec3Dot(lua_State *L) {
/**
* Returns the dot product of two vec3 values.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (number).
*/
static int moduleVec3Dot(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
lua_pushnumber(L, (lua_Number)glm_vec3_dot(*a, *b)); return 1;
assertNotNull(b, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_dot(*a, *b));
return 1;
}
static int vec3Cross(lua_State *L) {
/**
* Returns the cross product of two vec3 values as a new vec3.
*
* @param L Lua state. Arg 1: vec3. Arg 2: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3Cross(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
vec3 r; glm_vec3_cross(*a, *b, r);
luaVec3Push(L, r); return 1;
assertNotNull(b, "invalid vec3 userdata");
vec3 r;
glm_vec3_cross(*a, *b, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3Length(lua_State *L) {
/**
* Returns the length (magnitude) of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (number).
*/
static int moduleVec3Length(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
lua_pushnumber(L, (lua_Number)glm_vec3_norm(*v)); return 1;
assertNotNull(v, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_norm(*v));
return 1;
}
static int vec3LengthSq(lua_State *L) {
/**
* Returns the squared length of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (number).
*/
static int moduleVec3LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
lua_pushnumber(L, (lua_Number)glm_vec3_norm2(*v)); return 1;
assertNotNull(v, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_norm2(*v));
return 1;
}
static int vec3Normalize(lua_State *L) {
/**
* Returns a normalized copy of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
vec3 r; glm_vec3_normalize_to(*v, r);
luaVec3Push(L, r); return 1;
assertNotNull(v, "invalid vec3 userdata");
vec3 r;
glm_vec3_normalize_to(*v, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3Lerp(lua_State *L) {
/**
* Linearly interpolates between two vec3 values.
*
* @param L Lua state. Arg 1: vec3 a. Arg 2: vec3 b. Arg 3: number t.
* @return 1 (new vec3).
*/
static int moduleVec3Lerp(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec3 r; glm_vec3_lerp(*a, *b, t, r);
luaVec3Push(L, r); return 1;
vec3 r;
glm_vec3_lerp(*a, *b, t, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3Distance(lua_State *L) {
/**
* Returns the distance between two vec3 points.
*
* @param L Lua state. Arg 1: vec3 a. Arg 2: vec3 b.
* @return 1 (number).
*/
static int moduleVec3Distance(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
lua_pushnumber(L, (lua_Number)glm_vec3_distance(*a, *b)); return 1;
assertNotNull(b, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_distance(*a, *b));
return 1;
}
static int vec3Negate(lua_State *L) {
/**
* Returns a negated copy of a vec3.
*
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
vec3 r; glm_vec3_negate_to(*v, r);
luaVec3Push(L, r); return 1;
assertNotNull(v, "invalid vec3 userdata");
vec3 r;
glm_vec3_negate_to(*v, r);
moduleVec3Push(L, r);
return 1;
}
static int vec3Create(lua_State *L) {
/**
* Constructor: creates a vec3 from three optional numbers (defaults to 0).
*
* @param L Lua state. Arg 1 (optional): x. Arg 2 (optional): y. Arg 3 (optional): z.
* @return 1 (new vec3).
*/
static int moduleVec3Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 v = {0, 0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
if(top >= 3) v[2] = (float_t)luaL_checknumber(L, 3);
luaVec3Push(L, v); return 1;
moduleVec3Push(L, v);
return 1;
}
static void moduleMathVec3(lua_State *L) {
if(!luaL_newmetatable(L, "vec3_mt")) { lua_pop(L, 1); return; }
/**
* Registers the vec3 metatable and vec3 constructor global.
*
* @param L Lua state.
*/
static void moduleVec3(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_pushcfunction(L, vec3Index); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, vec3Newindex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, vec3OpAdd); lua_setfield(L, -2, "__add");
lua_pushcfunction(L, vec3OpSub); lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, vec3OpMul); lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, vec3OpDiv); lua_setfield(L, -2, "__div");
lua_pushcfunction(L, vec3OpUnm); lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, vec3OpEq); lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, vec3OpToString); lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, vec3Dot); lua_setfield(L, -2, "dot");
lua_pushcfunction(L, vec3Cross); lua_setfield(L, -2, "cross");
lua_pushcfunction(L, vec3Length); lua_setfield(L, -2, "length");
lua_pushcfunction(L, vec3LengthSq); lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, vec3Normalize); lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, vec3Lerp); lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, vec3Distance); lua_setfield(L, -2, "distance");
lua_pushcfunction(L, vec3Negate); lua_setfield(L, -2, "negate");
if(!luaL_newmetatable(L, "vec3_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec3Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec3NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec3OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec3OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec3OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec3OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec3OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec3OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec3OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec3Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec3Cross);
lua_setfield(L, -2, "cross");
lua_pushcfunction(L, moduleVec3Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec3LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec3Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec3Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec3Distance);
lua_setfield(L, -2, "distance");
lua_pushcfunction(L, moduleVec3Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec3", vec3Create);
lua_register(L, "vec3", moduleVec3Create);
}
+358 -75
View File
@@ -8,161 +8,444 @@
#pragma once
#include "script/module/modulebase.h"
static void luaVec4Push(lua_State *L, vec4 v) {
/**
* Pushes a new vec4 userdata onto the Lua stack with the vec4_mt metatable.
*
* @param L Lua state.
* @param v Source vector to copy.
*/
static void moduleVec4Push(lua_State *L, vec4 v) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *u = (vec4 *)lua_newuserdata(L, sizeof(vec4));
glm_vec4_copy(v, *u);
luaL_getmetatable(L, "vec4_mt");
lua_setmetatable(L, -2);
}
static void luaVec4Check(lua_State *L, int idx, vec4 out) {
/**
* Reads a vec4 userdata from the given stack index into out.
*
* @param L Lua state.
* @param idx Stack index of the vec4 userdata.
* @param out Destination vector.
*/
static void moduleVec4Check(lua_State *L, int idx, vec4 out) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, idx, "vec4_mt");
glm_vec4_copy(*v, out);
}
static int vec4Index(lua_State *L) {
/**
* __index metamethod for vec4 userdata.
* Supports integer indices 1-4 and string keys x/u0, y/v0, z/u1, w/v1;
* falls through to the metatable for method lookups.
*
* @param L Lua state. Arg 1: vec4 userdata. Arg 2: integer or string key.
* @return 1.
*/
static int moduleVec4Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
const char *key = luaL_checkstring(L, 2);
if(stringCompare(key, "x") == 0 || stringCompare(key, "u0") == 0) { lua_pushnumber(L, (lua_Number)(*v)[0]); return 1; }
if(stringCompare(key, "y") == 0 || stringCompare(key, "v0") == 0) { lua_pushnumber(L, (lua_Number)(*v)[1]); return 1; }
if(stringCompare(key, "z") == 0 || stringCompare(key, "u1") == 0) { lua_pushnumber(L, (lua_Number)(*v)[2]); return 1; }
if(stringCompare(key, "w") == 0 || stringCompare(key, "v1") == 0) { lua_pushnumber(L, (lua_Number)(*v)[3]); return 1; }
assertStrLenMin(key, 1, "property key cannot be empty");
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 4) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringEquals(key, "x") || stringEquals(key, "u0")) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringEquals(key, "y") || stringEquals(key, "v0")) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
if(stringEquals(key, "z") || stringEquals(key, "u1")) {
lua_pushnumber(L, (lua_Number)(*v)[2]);
return 1;
}
if(stringEquals(key, "w") || stringEquals(key, "v1")) {
lua_pushnumber(L, (lua_Number)(*v)[3]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
}
static int vec4Newindex(lua_State *L) {
/**
* __newindex metamethod for vec4 userdata.
* Writes x/u0, y/v0, z/u1, w/v1. Errors on unknown keys.
*
* @param L Lua state. Arg 1: vec4 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec4NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
const char *key = luaL_checkstring(L, 2);
if(stringCompare(key, "x") == 0 || stringCompare(key, "u0") == 0) { (*v)[0] = (float_t)luaL_checknumber(L, 3); return 0; }
if(stringCompare(key, "y") == 0 || stringCompare(key, "v0") == 0) { (*v)[1] = (float_t)luaL_checknumber(L, 3); return 0; }
if(stringCompare(key, "z") == 0 || stringCompare(key, "u1") == 0) { (*v)[2] = (float_t)luaL_checknumber(L, 3); return 0; }
if(stringCompare(key, "w") == 0 || stringCompare(key, "v1") == 0) { (*v)[3] = (float_t)luaL_checknumber(L, 3); return 0; }
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringEquals(key, "x") || stringEquals(key, "u0")) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "y") || stringEquals(key, "v0")) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "z") || stringEquals(key, "u1")) {
(*v)[2] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "w") || stringEquals(key, "v1")) {
(*v)[3] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec4: unknown property '%s'", key);
return 0;
}
static int vec4OpAdd(lua_State *L) {
/**
* __add metamethod: returns a + b as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
vec4 r; glm_vec4_add(*a, *b, r);
luaVec4Push(L, r); return 1;
assertNotNull(b, "invalid vec4 userdata");
vec4 r;
glm_vec4_add(*a, *b, r);
moduleVec4Push(L, r);
return 1;
}
static int vec4OpSub(lua_State *L) {
/**
* __sub metamethod: returns a - b as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
vec4 r; glm_vec4_sub(*a, *b, r);
luaVec4Push(L, r); return 1;
assertNotNull(b, "invalid vec4 userdata");
vec4 r;
glm_vec4_sub(*a, *b, r);
moduleVec4Push(L, r);
return 1;
}
static int vec4OpMul(lua_State *L) {
/**
* __mul metamethod: returns v * scalar or scalar * v as a new vec4.
*
* @param L Lua state. Arg 1: vec4 or number. Arg 2: number or vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec4 *v = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
glm_vec4_scale(*v, s, r);
} else {
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec4_scale(*v, s, r);
}
luaVec4Push(L, r); return 1;
}
static int vec4OpDiv(lua_State *L) {
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
float_t s = (float_t)luaL_checknumber(L, 2);
vec4 r; glm_vec4_divs(*v, s, r);
luaVec4Push(L, r); return 1;
}
static int vec4OpUnm(lua_State *L) {
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
vec4 r; glm_vec4_negate_to(*v, r);
luaVec4Push(L, r); return 1;
}
static int vec4OpEq(lua_State *L) {
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] && (*a)[2] == (*b)[2] && (*a)[3] == (*b)[3]);
moduleVec4Push(L, r);
return 1;
}
static int vec4OpToString(lua_State *L) {
/**
* __div metamethod: returns v / scalar as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: number.
* @return 1 (new vec4).
*/
static int moduleVec4OpDiv(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
vec4 r;
glm_vec4_divs(*v, s, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __unm metamethod: returns -v as a new vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpUnm(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_negate_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __eq metamethod: component-wise equality test.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (boolean).
*/
static int moduleVec4OpEq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
lua_pushboolean(L,
(*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] &&
(*a)[2] == (*b)[2] && (*a)[3] == (*b)[3]
);
return 1;
}
/**
* __tostring metamethod: returns a human-readable representation.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (string).
*/
static int moduleVec4OpToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
char buf[96];
snprintf(buf, sizeof(buf), "vec4(%.3f, %.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2], (*v)[3]);
lua_pushstring(L, buf); return 1;
lua_pushstring(L, buf);
return 1;
}
static int vec4Dot(lua_State *L) {
/**
* Returns the dot product of two vec4 values.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (number).
*/
static int moduleVec4Dot(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
lua_pushnumber(L, (lua_Number)glm_vec4_dot(*a, *b)); return 1;
assertNotNull(b, "invalid vec4 userdata");
lua_pushnumber(L, (lua_Number)glm_vec4_dot(*a, *b));
return 1;
}
static int vec4Length(lua_State *L) {
/**
* Returns the length (magnitude) of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (number).
*/
static int moduleVec4Length(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
lua_pushnumber(L, (lua_Number)glm_vec4_norm(*v)); return 1;
assertNotNull(v, "invalid vec4 userdata");
lua_pushnumber(L, (lua_Number)glm_vec4_norm(*v));
return 1;
}
static int vec4LengthSq(lua_State *L) {
/**
* Returns the squared length of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (number).
*/
static int moduleVec4LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
lua_pushnumber(L, (lua_Number)glm_vec4_norm2(*v)); return 1;
assertNotNull(v, "invalid vec4 userdata");
lua_pushnumber(L, (lua_Number)glm_vec4_norm2(*v));
return 1;
}
static int vec4Normalize(lua_State *L) {
/**
* Returns a normalized copy of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
vec4 r; glm_vec4_normalize_to(*v, r);
luaVec4Push(L, r); return 1;
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_normalize_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
static int vec4Lerp(lua_State *L) {
/**
* Linearly interpolates between two vec4 values.
*
* @param L Lua state. Arg 1: vec4 a. Arg 2: vec4 b. Arg 3: number t.
* @return 1 (new vec4).
*/
static int moduleVec4Lerp(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec4 r; glm_vec4_lerp(*a, *b, t, r);
luaVec4Push(L, r); return 1;
vec4 r;
glm_vec4_lerp(*a, *b, t, r);
moduleVec4Push(L, r);
return 1;
}
static int vec4Negate(lua_State *L) {
/**
* Returns a negated copy of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
vec4 r; glm_vec4_negate_to(*v, r);
luaVec4Push(L, r); return 1;
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_negate_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
static int vec4Create(lua_State *L) {
/**
* Constructor: creates a vec4 from four optional numbers (defaults to 0).
*
* @param L Lua state. Args 1-4 (optional): x, y, z, w.
* @return 1 (new vec4).
*/
static int moduleVec4Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 v = {0, 0, 0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
if(top >= 3) v[2] = (float_t)luaL_checknumber(L, 3);
if(top >= 4) v[3] = (float_t)luaL_checknumber(L, 4);
luaVec4Push(L, v); return 1;
moduleVec4Push(L, v);
return 1;
}
static void moduleMathVec4(lua_State *L) {
if(!luaL_newmetatable(L, "vec4_mt")) { lua_pop(L, 1); return; }
/**
* Registers the vec4 metatable and vec4 constructor global.
*
* @param L Lua state.
*/
static void moduleVec4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
lua_pushcfunction(L, vec4Index); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, vec4Newindex); lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, vec4OpAdd); lua_setfield(L, -2, "__add");
lua_pushcfunction(L, vec4OpSub); lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, vec4OpMul); lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, vec4OpDiv); lua_setfield(L, -2, "__div");
lua_pushcfunction(L, vec4OpUnm); lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, vec4OpEq); lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, vec4OpToString); lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, vec4Dot); lua_setfield(L, -2, "dot");
lua_pushcfunction(L, vec4Length); lua_setfield(L, -2, "length");
lua_pushcfunction(L, vec4LengthSq); lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, vec4Normalize); lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, vec4Lerp); lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, vec4Negate); lua_setfield(L, -2, "negate");
if(!luaL_newmetatable(L, "vec4_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec4Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec4NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec4OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec4OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec4OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec4OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec4OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec4OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec4OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec4Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec4Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec4LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec4Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec4Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec4Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec4", vec4Create);
lua_register(L, "vec4", moduleVec4Create);
}
+2
View File
@@ -22,6 +22,7 @@
#include "script/module/display/modulescreen.h"
#include "script/module/display/moduletexture.h"
#include "script/module/display/moduletileset.h"
#include "script/module/scene/modulescene.h"
void moduleRegister(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
@@ -42,4 +43,5 @@ void moduleRegister(lua_State *L) {
moduleScreen(L);
moduleTexture(L);
moduleTileset(L);
moduleScene(L);
}
+126
View File
@@ -0,0 +1,126 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/module/modulebase.h"
#include "scene/scene.h"
/**
* __index metamethod for the Scene table. Handles dynamic read-only properties.
*
* @param L Lua state. Arg 1: table, Arg 2: key.
* @return 1.
*/
static int moduleSceneIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
const char_t *key = lua_tostring(L, 2);
assertNotNull(key, "Scene property key cannot be NULL");
// if(stringEquals(key, "current")) {
// if(SCENE.sceneActive) {
// lua_pushstring(L, SCENE.sceneCurrent);
// } else {
// lua_pushnil(L);
// }
// return 1;
// }
lua_pushnil(L);
return 1;
}
/**
* Attached Scene.set method to invoke internal C method.
*
* @param L Lua state.
* @return Number of return values on the Lua stack.
*/
static int moduleSceneSet(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "Scene.set requires a string argument");
return 0;
}
sceneSet(lua_tostring(L, 1));
return 0;
}
/**
* Resets the scene back to a clean slate, this is called before loading a new
* scene to ensure that no old state bleeds through.
*/
static void moduleSceneReset(lua_State *L) {
if(SCENE.scriptRef != LUA_NOREF) {
luaL_unref(L, LUA_REGISTRYINDEX, SCENE.scriptRef);
SCENE.scriptRef = LUA_NOREF;
}
lua_newtable(L);
// Scene.set
lua_pushcfunction(L, moduleSceneSet);
lua_setfield(L, -2, "set");
// Metatable for dynamic read-only properties (e.g. Scene.current)
lua_newtable(L);
lua_pushcfunction(L, moduleSceneIndex);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_setglobal(L, "Scene");
}
/**
* Invokes a method on the current scene.
*
* @param method Which method to call.
* @return Any error state that happened.
*/
static errorret_t moduleSceneCall(lua_State *L, const char_t *method) {
assertNotNull(L, "Lua state cannot be NULL");
assertStrLenMin(method, 1, "Method name cannot be empty");
assertTrue(
SCENE.scriptRef != LUA_NOREF && SCENE.scriptRef != LUA_REFNIL,
"No active scene script to call method on"
);
// Get the scene table
lua_rawgeti(L, LUA_REGISTRYINDEX, SCENE.scriptRef);
if(!lua_istable(L, -1)) {
lua_pop(L, 1);
errorThrow("Scene script ref %d is not a table", SCENE.scriptRef);
}
// Get the method from the scene table
lua_getfield(L, -1, method);
if(!lua_isfunction(L, -1)) {
lua_pop(L, 2);
errorThrow("Scene method '%s' not found", method);// TODO: Needed?
}
// Push the scene table as the first argument (self)
lua_pushvalue(L, -2);
// Call the method with 1 argument (the scene table) and 0 return values
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
const char_t *err = lua_tostring(L, -1);
lua_pop(L, 2);// Pops the error message and the scene table
errorThrow("Scene:%s failed: %s", method, err);
}
lua_pop(L, 1);// Pops the scene table
errorOk();
}
static void moduleScene(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
moduleSceneReset(L);
}
+4
View File
@@ -28,6 +28,10 @@ int stringCompare(const char_t *str1, const char_t *str2) {
return strcmp(str1, str2);
}
bool_t stringEquals(const char_t *str1, const char_t *str2) {
return stringCompare(str1, str2) == 0;
}
int stringCompareInsensitive(const char_t *str1, const char_t *str2) {
assertNotNull(str1, "str1 must not be NULL");
assertNotNull(str2, "str2 must not be NULL");
+9
View File
@@ -36,6 +36,15 @@ void stringCopy(char_t *dest, const char_t *src, const size_t destSize);
*/
int stringCompare(const char_t *str1, const char_t *str2);
/**
* Compares two strings for equality.
*
* @param str1 The first string.
* @param str2 The second string.
* @return TRUE if the strings are equal, FALSE otherwise.
*/
bool_t stringEquals(const char_t *str1, const char_t *str2);
/**
* Compares two strings, ignoring case.
*