From 17c924f040f608368a7d921c9049541205987e8a Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Tue, 21 Apr 2026 08:39:50 -0500 Subject: [PATCH] Playertest first pass --- assets/entities/plane.lua | 17 + assets/entities/player.lua | 46 +++ .../entity/component/display/entitycamera.c | 36 +++ .../entity/component/display/entitycamera.h | 25 +- .../entity/component/display/entitymaterial.c | 11 + .../entity/component/display/entitymaterial.h | 13 + .../entity/component/display/entitymesh.c | 104 +++++- .../entity/component/display/entitymesh.h | 46 +++ .../entity/component/physics/entityphysics.c | 19 ++ .../entity/component/physics/entityphysics.h | 25 ++ src/dusk/entity/componentlist.h | 2 +- src/dusk/event/event.c | 58 ++-- src/dusk/scene/scene.c | 3 + src/dusk/scene/scene.h | 6 + src/dusk/script/module/CMakeLists.txt | 1 + src/dusk/script/module/display/CMakeLists.txt | 1 - src/dusk/script/module/display/moduleglm.c | 274 +--------------- src/dusk/script/module/display/moduleglm.h | 71 +--- .../entity/display/moduleentitycamera.c | 173 ++++++---- .../entity/display/moduleentitycamera.h | 6 - .../entity/display/moduleentitymaterial.c | 64 +++- .../entity/display/moduleentitymaterial.h | 2 - .../module/entity/display/moduleentitymesh.c | 75 ++++- .../module/entity/display/moduleentitymesh.h | 2 - .../entity/display/moduleentityposition.c | 261 ++++++++------- .../entity/display/moduleentityposition.h | 9 - .../entity/physics/moduleentityphysics.c | 303 +++++++++++------- .../entity/physics/moduleentityphysics.h | 10 - src/dusk/script/module/event/moduleevent.c | 21 +- src/dusk/script/module/event/moduleevent.h | 7 +- src/dusk/script/module/math/CMakeLists.txt | 13 + src/dusk/script/module/math/modulemat4.c | 117 +++++++ src/dusk/script/module/math/modulemat4.h | 13 + src/dusk/script/module/math/modulemath.c | 22 ++ src/dusk/script/module/math/modulemath.h | 11 + src/dusk/script/module/math/modulevec2.c | 169 ++++++++++ src/dusk/script/module/math/modulevec2.h | 13 + src/dusk/script/module/math/modulevec3.c | 180 +++++++++++ src/dusk/script/module/math/modulevec3.h | 13 + src/dusk/script/module/math/modulevec4.c | 168 ++++++++++ src/dusk/script/module/math/modulevec4.h | 13 + src/dusk/script/module/scene/modulescene.c | 3 + src/dusk/script/scriptmodule.c | 2 + 43 files changed, 1701 insertions(+), 727 deletions(-) create mode 100644 assets/entities/plane.lua create mode 100644 assets/entities/player.lua create mode 100644 src/dusk/script/module/math/CMakeLists.txt create mode 100644 src/dusk/script/module/math/modulemat4.c create mode 100644 src/dusk/script/module/math/modulemat4.h create mode 100644 src/dusk/script/module/math/modulemath.c create mode 100644 src/dusk/script/module/math/modulemath.h create mode 100644 src/dusk/script/module/math/modulevec2.c create mode 100644 src/dusk/script/module/math/modulevec2.h create mode 100644 src/dusk/script/module/math/modulevec3.c create mode 100644 src/dusk/script/module/math/modulevec3.h create mode 100644 src/dusk/script/module/math/modulevec4.c create mode 100644 src/dusk/script/module/math/modulevec4.h diff --git a/assets/entities/plane.lua b/assets/entities/plane.lua new file mode 100644 index 00000000..a1b45316 --- /dev/null +++ b/assets/entities/plane.lua @@ -0,0 +1,17 @@ +module('entity') + +function entityInit() + entityPositionAdd(ENTITY_ID) + + local phys = entityPhysicsAdd(ENTITY_ID) + phys.bodyType = PHYSICS_BODY_STATIC + phys:setShapePlane(0, 1, 0, 0) + + local mesh = entityMeshAdd(ENTITY_ID) + mesh:generatePlane(20, 20) + + entityMaterialAdd(ENTITY_ID) +end + +function entityDispose() +end diff --git a/assets/entities/player.lua b/assets/entities/player.lua new file mode 100644 index 00000000..d54dabaf --- /dev/null +++ b/assets/entities/player.lua @@ -0,0 +1,46 @@ +module('entity') +module('input') +module('color') +module('math') +module('scene') +module('event') +module('entitycamera') + +local phys +local updateSub +local SPEED = 4.0 + +function entityInit() + local pos = entityPositionAdd(ENTITY_ID) + pos.x = 0 + pos.y = 1 + pos.z = 0 + + phys = entityPhysicsAdd(ENTITY_ID) + phys:setShapeCapsule(0.3, 0.9) + + local mesh = entityMeshAdd(ENTITY_ID) + mesh:generateCapsule(0.3, 0.9) + + local mat = entityMaterialAdd(ENTITY_ID) + mat.color = colorBlue() + + updateSub = eventSubscribe(SCENE_EVENT_UPDATE, onUpdate) +end + +function onUpdate() + local moveX = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT) + local moveZ = inputAxis(INPUT_ACTION_DOWN, INPUT_ACTION_UP) + + local cam = entityCameraGetCurrent() + local fwd = cam:getForward() + local right = cam:getRight() + + local vy = phys.velocityY + phys.velocityX = (moveX * right.x + moveZ * fwd.x) * SPEED + phys.velocityZ = (moveX * right.y + moveZ * fwd.y) * SPEED +end + +function entityDispose() + eventUnsubscribe(SCENE_EVENT_UPDATE, updateSub) +end diff --git a/src/dusk/entity/component/display/entitycamera.c b/src/dusk/entity/component/display/entitycamera.c index fd1b238a..ae3d1c53 100644 --- a/src/dusk/entity/component/display/entitycamera.c +++ b/src/dusk/entity/component/display/entitycamera.c @@ -6,6 +6,8 @@ */ #include "entity/entitymanager.h" +#include "entity/entity.h" +#include "entity/component/display/entityposition.h" #include "display/framebuffer/framebuffer.h" #include "display/screen/screen.h" @@ -92,4 +94,38 @@ void entityCameraGetProjection( out ); } +} + +entityid_t entityCameraGetCurrent(void) { + entityid_t camEnts[ENTITY_COUNT_MAX]; + componentid_t camComps[ENTITY_COUNT_MAX]; + entityid_t count = componentGetEntitiesWithComponent( + COMPONENT_TYPE_CAMERA, camEnts, camComps + ); + if(count == 0) return ENTITY_COUNT_MAX; + return camEnts[0]; +} + +void entityCameraGetForward(const entityid_t entityId, vec2 out) { + componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); + entityposition_t *pos = entityPositionGet(entityId, posComp); + // View matrix column layout: M[col][row], forward = {-M[0][2], -M[1][2], -M[2][2]} + float_t fx = -pos->transform[0][2]; + float_t fz = -pos->transform[2][2]; + float_t len = sqrtf(fx * fx + fz * fz); + if(len > 1e-6f) { fx /= len; fz /= len; } + out[0] = fx; + out[1] = fz; +} + +void entityCameraGetRight(const entityid_t entityId, vec2 out) { + componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION); + entityposition_t *pos = entityPositionGet(entityId, posComp); + // View matrix column layout: right = {M[0][0], M[1][0], M[2][0]} + float_t rx = pos->transform[0][0]; + float_t rz = pos->transform[2][0]; + float_t len = sqrtf(rx * rx + rz * rz); + if(len > 1e-6f) { rx /= len; rz /= len; } + out[0] = rx; + out[1] = rz; } \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitycamera.h b/src/dusk/entity/component/display/entitycamera.h index a258262e..59b6fa46 100644 --- a/src/dusk/entity/component/display/entitycamera.h +++ b/src/dusk/entity/component/display/entitycamera.h @@ -96,4 +96,27 @@ void entityCameraSetZFar( const entityid_t ent, const componentid_t comp, const float_t zFar -); \ No newline at end of file +); + +/** + * Returns the entity ID of the first active camera, or ENTITY_COUNT_MAX if none. + */ +entityid_t entityCameraGetCurrent(void); + +/** + * Gets the camera's horizontal forward direction (XZ plane) from its position + * component. Automatically finds the position component on the entity. + * + * @param entityId The camera entity ID. + * @param out Output vec2: {forwardX, forwardZ} normalized. + */ +void entityCameraGetForward(const entityid_t entityId, vec2 out); + +/** + * Gets the camera's horizontal right direction (XZ plane) from its position + * component. Automatically finds the position component on the entity. + * + * @param entityId The camera entity ID. + * @param out Output vec2: {rightX, rightZ} normalized. + */ +void entityCameraGetRight(const entityid_t entityId, vec2 out); \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymaterial.c b/src/dusk/entity/component/display/entitymaterial.c index 5aa7d9a0..da65d78d 100644 --- a/src/dusk/entity/component/display/entitymaterial.c +++ b/src/dusk/entity/component/display/entitymaterial.c @@ -48,4 +48,15 @@ void entityMaterialSetShader( entityId, componentId, COMPONENT_TYPE_MATERIAL ); mat->shader = shader; +} + +void entityMaterialSetColor( + const entityid_t entityId, + const componentid_t componentId, + const color_t color +) { + entitymaterial_t *mat = componentGetData( + entityId, componentId, COMPONENT_TYPE_MATERIAL + ); + mat->material.unlit.color = color; } \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymaterial.h b/src/dusk/entity/component/display/entitymaterial.h index c4bd1e4f..341ff0d0 100644 --- a/src/dusk/entity/component/display/entitymaterial.h +++ b/src/dusk/entity/component/display/entitymaterial.h @@ -60,4 +60,17 @@ void entityMaterialSetShader( const entityid_t entityId, const componentid_t componentId, shader_t *shader +); + +/** + * Sets the unlit color for the given entity and component. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param color The color to set. + */ +void entityMaterialSetColor( + const entityid_t entityId, + const componentid_t componentId, + const color_t color ); \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymesh.c b/src/dusk/entity/component/display/entitymesh.c index cab59b3c..fa5f2882 100644 --- a/src/dusk/entity/component/display/entitymesh.c +++ b/src/dusk/entity/component/display/entitymesh.c @@ -5,9 +5,13 @@ * https://opensource.org/licenses/MIT */ +#include "entitymesh.h" #include "entity/entitymanager.h" - +#include "assert/assert.h" +#include "util/memory.h" #include "display/mesh/cube.h" +#include "display/mesh/plane.h" +#include "display/mesh/capsule.h" void entityMeshInit( const entityid_t entityId, @@ -17,6 +21,8 @@ void entityMeshInit( entityId, componentId, COMPONENT_TYPE_MESH ); comp->mesh = &CUBE_MESH_SIMPLE; + comp->ownedVertices = NULL; + comp->ownsData = false; } mesh_t * entityMeshGetMesh( @@ -38,4 +44,100 @@ void entityMeshSetMesh( entityId, componentId, COMPONENT_TYPE_MESH ); comp->mesh = mesh; +} + +void entityMeshDispose( + const entityid_t entityId, + const componentid_t componentId +) { + entitymesh_t *comp = componentGetData( + entityId, componentId, COMPONENT_TYPE_MESH + ); + if(!comp->ownsData) return; + (void)meshDispose(&comp->ownedMesh); + memoryFree(comp->ownedVertices); + comp->ownedVertices = NULL; + comp->ownsData = false; + comp->mesh = NULL; +} + +errorret_t entityMeshGeneratePlane( + const entityid_t entityId, + const componentid_t componentId, + const float_t width, + const float_t height +) { + entitymesh_t *comp = componentGetData( + entityId, componentId, COMPONENT_TYPE_MESH + ); + entityMeshDispose(entityId, componentId); + + comp->ownedVertices = memoryAllocate(PLANE_VERTEX_COUNT * sizeof(meshvertex_t)); + assertNotNull(comp->ownedVertices, "Failed to allocate plane vertices"); + + vec3 min = { -width * 0.5f, 0.0f, -height * 0.5f }; + vec3 max = { width * 0.5f, 0.0f, height * 0.5f }; + vec2 uvMin = { 0.0f, 0.0f }; + vec2 uvMax = { 1.0f, 1.0f }; + planeBuffer( + comp->ownedVertices, + PLANE_AXIS_XZ, + min, + max + #if MESH_ENABLE_COLOR + , COLOR_WHITE_4B + #endif + , uvMin, + uvMax + ); + + errorChain(meshInit( + &comp->ownedMesh, + PLANE_PRIMITIVE_TYPE, + PLANE_VERTEX_COUNT, + comp->ownedVertices + )); + + comp->mesh = &comp->ownedMesh; + comp->ownsData = true; + errorOk(); +} + +errorret_t entityMeshGenerateCapsule( + const entityid_t entityId, + const componentid_t componentId, + const float_t radius, + const float_t halfHeight +) { + entitymesh_t *comp = componentGetData( + entityId, componentId, COMPONENT_TYPE_MESH + ); + entityMeshDispose(entityId, componentId); + + comp->ownedVertices = memoryAllocate(CAPSULE_VERTEX_COUNT * sizeof(meshvertex_t)); + assertNotNull(comp->ownedVertices, "Failed to allocate capsule vertices"); + + vec3 center = { 0.0f, 0.0f, 0.0f }; + capsuleBuffer( + comp->ownedVertices, + center, + radius, + halfHeight, + CAPSULE_CAP_RINGS, + CAPSULE_SECTORS + #if MESH_ENABLE_COLOR + , COLOR_WHITE_4B + #endif + ); + + errorChain(meshInit( + &comp->ownedMesh, + CAPSULE_PRIMITIVE_TYPE, + CAPSULE_VERTEX_COUNT, + comp->ownedVertices + )); + + comp->mesh = &comp->ownedMesh; + comp->ownsData = true; + errorOk(); } \ No newline at end of file diff --git a/src/dusk/entity/component/display/entitymesh.h b/src/dusk/entity/component/display/entitymesh.h index 83bc0876..10fb8b5f 100644 --- a/src/dusk/entity/component/display/entitymesh.h +++ b/src/dusk/entity/component/display/entitymesh.h @@ -11,6 +11,9 @@ typedef struct { mesh_t *mesh; + mesh_t ownedMesh; + meshvertex_t *ownedVertices; + bool_t ownsData; } entitymesh_t; /** @@ -47,4 +50,47 @@ void entityMeshSetMesh( const entityid_t entityId, const componentid_t componentId, mesh_t *mesh +); + +/** + * Disposes the entity mesh component, freeing any owned mesh data. + * + * @param entityId The entity ID. + * @param componentId The component ID. + */ +void entityMeshDispose( + const entityid_t entityId, + const componentid_t componentId +); + +/** + * Generates an XZ-aligned plane mesh owned by the component. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param width Width of the plane along the X axis. + * @param height Height of the plane along the Z axis. + * @return Error return value. + */ +errorret_t entityMeshGeneratePlane( + const entityid_t entityId, + const componentid_t componentId, + const float_t width, + const float_t height +); + +/** + * Generates a Y-axis capsule mesh owned by the component. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param radius Radius of the cylinder and hemisphere caps. + * @param halfHeight Half-height of the cylindrical section. + * @return Error return value. + */ +errorret_t entityMeshGenerateCapsule( + const entityid_t entityId, + const componentid_t componentId, + const float_t radius, + const float_t halfHeight ); \ No newline at end of file diff --git a/src/dusk/entity/component/physics/entityphysics.c b/src/dusk/entity/component/physics/entityphysics.c index 631baece..3705f8b7 100644 --- a/src/dusk/entity/component/physics/entityphysics.c +++ b/src/dusk/entity/component/physics/entityphysics.c @@ -100,6 +100,25 @@ bool_t entityPhysicsIsOnGround( return phys->onGround; } +void entityPhysicsSetBodyType( + const entityid_t entityId, + const componentid_t componentId, + const physicsbodytype_t type +) { + entityphysics_t *phys = entityPhysicsGet(entityId, componentId); + assertNotNull(phys, "Failed to get physics component data"); + phys->type = type; +} + +physicsbodytype_t entityPhysicsGetBodyType( + const entityid_t entityId, + const componentid_t componentId +) { + entityphysics_t *phys = entityPhysicsGet(entityId, componentId); + assertNotNull(phys, "Failed to get physics component data"); + return phys->type; +} + void entityPhysicsDispose( const entityid_t entityId, const componentid_t componentId diff --git a/src/dusk/entity/component/physics/entityphysics.h b/src/dusk/entity/component/physics/entityphysics.h index 069f97c3..6cccfca3 100644 --- a/src/dusk/entity/component/physics/entityphysics.h +++ b/src/dusk/entity/component/physics/entityphysics.h @@ -122,6 +122,31 @@ bool_t entityPhysicsIsOnGround( const componentid_t componentId ); +/** + * Sets the body type of the entity's physics body. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @param type The body type to set. + */ +void entityPhysicsSetBodyType( + const entityid_t entityId, + const componentid_t componentId, + const physicsbodytype_t type +); + +/** + * Gets the body type of the entity's physics body. + * + * @param entityId The entity ID. + * @param componentId The component ID. + * @return The body type of the physics body. + */ +physicsbodytype_t entityPhysicsGetBodyType( + const entityid_t entityId, + const componentid_t componentId +); + /** * Releases the body slot back to PHYSICS_WORLD. Called automatically when * the component is disposed via the component system. diff --git a/src/dusk/entity/componentlist.h b/src/dusk/entity/componentlist.h index 7ce59b84..939f3514 100644 --- a/src/dusk/entity/componentlist.h +++ b/src/dusk/entity/componentlist.h @@ -14,7 +14,7 @@ X(POSITION, entityposition_t, position, entityPositionInit, NULL) X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL) -X(MESH, entitymesh_t, mesh, entityMeshInit, NULL) +X(MESH, entitymesh_t, mesh, entityMeshInit, entityMeshDispose) X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL) X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose) X(ENTITYSCRIPT, entityscript_t, entityscript, entityScriptInit, entityScriptDispose) \ No newline at end of file diff --git a/src/dusk/event/event.c b/src/dusk/event/event.c index f0975d5e..178a472d 100644 --- a/src/dusk/event/event.c +++ b/src/dusk/event/event.c @@ -73,7 +73,7 @@ eventsub_t eventSubscribe( const eventcallback_t callback, const void *user ) { - eventSubscribeUser( + return eventSubscribeUser( event, EVENT_TYPE_C, (eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } } @@ -147,30 +147,29 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) { if(event->listenerCount == 0) return; - // Find listener uint16_t index = 0; do { - if(event->listenerArray[index].id == id) { - // Found it, remove by swapping with last and reducing count - event->listenerArray[index] = event->listenerArray[--event->listenerCount]; - return; + if(event->listenerArray[index].id != id) { + index++; + continue; } - index++; + + // Release Lua registry reference before the slot is overwritten + if(event->listenerArray[index].type == EVENT_TYPE_SCRIPT) { + scriptcontext_t *ctx = event->listenerArray[index].user.script.context; + if(ctx != NULL && ctx->luaState != NULL) { + luaL_unref( + ctx->luaState, + LUA_REGISTRYINDEX, + event->listenerArray[index].user.script.luaFunctionRef + ); + } + } + + // Swap with last and shrink + event->listenerArray[index] = event->listenerArray[--event->listenerCount]; + return; } while(index < event->listenerCount); - - // Did we find it? - if(index == event->listenerCount) return; - - // Shift remaining listeners down (if any) - if(index < event->listenerCount - 1) { - memoryMove( - &event->listenerArray[index], - &event->listenerArray[index + 1], - sizeof(eventlistener_t) * (event->listenerCount - index - 1) - ); - } - - event->listenerCount--; } void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) { @@ -219,27 +218,20 @@ void eventInvoke( if(listener->type == EVENT_TYPE_C) { listener->user.c.callback(&data, listener->user.c); } else if(listener->type == EVENT_TYPE_SCRIPT) { - // Call Lua function lua_State *L = listener->user.script.context->luaState; assertNotNull(L, "Lua state in event listener cannot be NULL"); - // Push function lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef); - if(eventParams != NULL && metatableName != NULL) { - lua_getmetatable(L, -1); - luaL_getmetatable(L, metatableName); - assertTrue( - lua_rawequal(L, -1, -2), - "Event parameter metatable does not match expected type" - ); + int numArgs = 0; + if(eventParams != NULL) { + lua_pushlightuserdata(L, (void *)eventParams); + numArgs = 1; } - // Call function with 1 arg, 0 return values - if(lua_pcall(L, 1, 0, 0) != LUA_OK) { + if(lua_pcall(L, numArgs, 0, 0) != LUA_OK) { const char_t *strErr = lua_tostring(L, -1); lua_pop(L, 1); - // Log error but continue printf("Error invoking Lua event listener: %s\n", strErr); } } else { diff --git a/src/dusk/scene/scene.c b/src/dusk/scene/scene.c index fb77aba0..339ad32e 100644 --- a/src/dusk/scene/scene.c +++ b/src/dusk/scene/scene.c @@ -22,6 +22,7 @@ scene_t SCENE; errorret_t sceneInit(void) { memoryZero(&SCENE, sizeof(scene_t)); + eventInit(&SCENE.eventUpdate, SCENE.updateListeners, SCENE_EVENT_UPDATE_MAX); errorOk(); } @@ -42,6 +43,8 @@ errorret_t sceneUpdate(void) { errorOk(); } + eventInvoke(&SCENE.eventUpdate, NULL, NULL); + assertNotNull(SCENE.script.luaState, "Script context null?"); lua_getglobal(SCENE.script.luaState, "sceneUpdate"); if(lua_isfunction(SCENE.script.luaState, -1)) { diff --git a/src/dusk/scene/scene.h b/src/dusk/scene/scene.h index f52ba82a..0dc382fa 100644 --- a/src/dusk/scene/scene.h +++ b/src/dusk/scene/scene.h @@ -8,12 +8,18 @@ #pragma once #include "script/scriptcontext.h" #include "asset/assetfile.h" +#include "event/event.h" + +#define SCENE_EVENT_UPDATE_MAX 16 typedef struct { scriptcontext_t script; bool_t scriptActive; char_t sceneCurrent[ASSET_FILE_PATH_MAX]; char_t sceneNext[ASSET_FILE_PATH_MAX]; + + eventlistener_t updateListeners[SCENE_EVENT_UPDATE_MAX]; + event_t eventUpdate; } scene_t; extern scene_t SCENE; diff --git a/src/dusk/script/module/CMakeLists.txt b/src/dusk/script/module/CMakeLists.txt index 4780d660..acc955d4 100644 --- a/src/dusk/script/module/CMakeLists.txt +++ b/src/dusk/script/module/CMakeLists.txt @@ -9,6 +9,7 @@ add_subdirectory(entity) add_subdirectory(event) add_subdirectory(input) add_subdirectory(locale) +add_subdirectory(math) add_subdirectory(system) add_subdirectory(scene) add_subdirectory(time) diff --git a/src/dusk/script/module/display/CMakeLists.txt b/src/dusk/script/module/display/CMakeLists.txt index 8283b3e5..86e38c6e 100644 --- a/src/dusk/script/module/display/CMakeLists.txt +++ b/src/dusk/script/module/display/CMakeLists.txt @@ -8,7 +8,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME} PUBLIC moduleglm.c modulespritebatch.c - moduleglm.c modulecolor.c moduletext.c modulescreen.c diff --git a/src/dusk/script/module/display/moduleglm.c b/src/dusk/script/module/display/moduleglm.c index 0a5af923..cc7119c2 100644 --- a/src/dusk/script/module/display/moduleglm.c +++ b/src/dusk/script/module/display/moduleglm.c @@ -1,283 +1,15 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "moduleglm.h" +#include "script/module/math/modulemath.h" #include "assert/assert.h" -#include "util/string.h" -#include "util/memory.h" void moduleGLM(scriptcontext_t *context) { assertNotNull(context, "Context cannot be NULL."); - - // Create metatable for vec3 structure. - if(luaL_newmetatable(context->luaState, "vec3_mt")) { - // Metatable methods - lua_pushcfunction(context->luaState, moduleVec3Index); - lua_setfield(context->luaState, -2, "__index"); - lua_pushcfunction(context->luaState, moduleVec3NewIndex); - lua_setfield(context->luaState, -2, "__newindex"); - lua_pushcfunction(context->luaState, moduleVec3ToString); - lua_setfield(context->luaState, -2, "__tostring"); - } - lua_pop(context->luaState, 1); - - if(luaL_newmetatable(context->luaState, "vec4_mt")) { - // Metatable methods - lua_pushcfunction(context->luaState, moduleVec4Index); - lua_setfield(context->luaState, -2, "__index"); - lua_pushcfunction(context->luaState, moduleVec4NewIndex); - lua_setfield(context->luaState, -2, "__newindex"); - lua_pushcfunction(context->luaState, moduleVec4ToString); - lua_setfield(context->luaState, -2, "__tostring"); - } - lua_pop(context->luaState, 1); - - lua_register(context->luaState, "vec3", moduleVec3Create); + moduleMath(context); } - - -int moduleVec3Create(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3)); - memoryZero(v, sizeof(vec3)); - - // May be expecting between 1 and 3 values. - int top = lua_gettop(l); - if(top >= 1) { - if(!lua_isnumber(l, 1)) { - luaL_error(l, "Vec3 x component must be a number."); - } - (*v)[0] = (float_t)lua_tonumber(l, 1); - } - if(top >= 2) { - if(!lua_isnumber(l, 2)) { - luaL_error(l, "Vec3 y component must be a number."); - } - (*v)[1] = (float_t)lua_tonumber(l, 2); - } - if(top >= 3) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 z component must be a number."); - } - (*v)[2] = (float_t)lua_tonumber(l, 3); - } - - // Set metatable - luaL_getmetatable(l, "vec3_mt"); - lua_setmetatable(l, -2); - - return 1; -} - -int moduleVec3Index(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = lua_tostring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); - assertNotNull(vec, "Vec3 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - lua_pushnumber(l, (*vec)[0]); - return 1; - } else if(stringCompare(key, "y") == 0) { - lua_pushnumber(l, (*vec)[1]); - return 1; - } else if(stringCompare(key, "z") == 0) { - lua_pushnumber(l, (*vec)[2]); - return 1; - } -} - -int moduleVec3NewIndex(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = luaL_checkstring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); - assertNotNull(vec, "Vec3 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 x component must be a number."); - } - (*vec)[0] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "y") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 y component must be a number."); - } - (*vec)[1] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "z") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec3 z component must be a number."); - } - (*vec)[2] = (float_t)lua_tonumber(l, 3); - return 0; - } - - luaL_error(l, "Invalid key for vec3: %s", key); - return 0; -} - -int moduleVec3ToString(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt"); - assertNotNull(vec, "Vec3 pointer cannot be NULL."); - - char buf[128]; - snprintf( - buf, sizeof(buf), - "vec3(%.3f, %.3f, %.3f)", - (*vec)[0], (*vec)[1], (*vec)[2] - ); - lua_pushstring(l, buf); - - return 1; -} - - - -int moduleVec4Create(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec4 *v = (vec4 *)lua_newuserdata(l, sizeof(vec4)); - memoryZero(v, sizeof(vec4)); - - // May be expecting between 1 and 4 values. - int top = lua_gettop(l); - if(top >= 1) { - if(!lua_isnumber(l, 1)) { - luaL_error(l, "Vec4 x component must be a number."); - } - (*v)[0] = (float_t)lua_tonumber(l, 1); - } - if(top >= 2) { - if(!lua_isnumber(l, 2)) { - luaL_error(l, "Vec4 y component must be a number."); - } - (*v)[1] = (float_t)lua_tonumber(l, 2); - } - if(top >= 3) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 z component must be a number."); - } - (*v)[2] = (float_t)lua_tonumber(l, 3); - } - if(top >= 4) { - if(!lua_isnumber(l, 4)) { - luaL_error(l, "Vec4 w component must be a number."); - } - (*v)[3] = (float_t)lua_tonumber(l, 4); - } - - // Set metatable - luaL_getmetatable(l, "vec4_mt"); - lua_setmetatable(l, -2); - - return 1; -} - -int moduleVec4Index(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = lua_tostring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt"); - assertNotNull(vec, "Vec4 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - lua_pushnumber(l, (*vec)[0]); - return 1; - } else if(stringCompare(key, "y") == 0) { - lua_pushnumber(l, (*vec)[1]); - return 1; - } else if(stringCompare(key, "z") == 0) { - lua_pushnumber(l, (*vec)[2]); - return 1; - } else if(stringCompare(key, "w") == 0) { - lua_pushnumber(l, (*vec)[3]); - return 1; - } else if(stringCompare(key, "u0") == 0) { - lua_pushnumber(l, (*vec)[0]); - return 1; - } else if(stringCompare(key, "v0") == 0) { - lua_pushnumber(l, (*vec)[1]); - return 1; - } else if(stringCompare(key, "u1") == 0) { - lua_pushnumber(l, (*vec)[2]); - return 1; - } else if(stringCompare(key, "v1") == 0) { - lua_pushnumber(l, (*vec)[3]); - return 1; - } - - lua_pushnil(l); - return 1; -} - -int moduleVec4NewIndex(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - const char_t *key = luaL_checkstring(l, 2); - assertStrLenMin(key, 1, "Key cannot be empty."); - - vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt"); - assertNotNull(vec, "Vec4 pointer cannot be NULL."); - - if(stringCompare(key, "x") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 x component must be a number."); - } - (*vec)[0] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "y") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 y component must be a number."); - } - (*vec)[1] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "z") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 z component must be a number."); - } - (*vec)[2] = (float_t)lua_tonumber(l, 3); - return 0; - } else if(stringCompare(key, "w") == 0) { - if(!lua_isnumber(l, 3)) { - luaL_error(l, "Vec4 w component must be a number."); - } - (*vec)[3] = (float_t)lua_tonumber(l, 3); - return 0; - } - - luaL_error(l, "Invalid key for vec4: %s", key); - return 0; -} - -int moduleVec4ToString(lua_State *l) { - assertNotNull(l, "Lua state cannot be NULL."); - - vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt"); - assertNotNull(vec, "Vec4 pointer cannot be NULL."); - - char buf[128]; - snprintf( - buf, sizeof(buf), - "vec4(%.3f, %.3f, %.3f, %.3f)", - (*vec)[0], (*vec)[1], (*vec)[2], (*vec)[3] - ); - lua_pushstring(l, buf); - - return 1; -} \ No newline at end of file diff --git a/src/dusk/script/module/display/moduleglm.h b/src/dusk/script/module/display/moduleglm.h index 12eb85ed..01a648e1 100644 --- a/src/dusk/script/module/display/moduleglm.h +++ b/src/dusk/script/module/display/moduleglm.h @@ -1,6 +1,6 @@ /** * Copyright (c) 2026 Dominic Masters - * + * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ @@ -8,73 +8,4 @@ #pragma once #include "script/scriptcontext.h" -/** - * Registers the GLM module with the given script context. - * - * @param context The script context to register the module with. - */ void moduleGLM(scriptcontext_t *context); - -/** - * Creates a new vec3 structure in Lua. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3Create(lua_State *l); - -/** - * Lua __index metamethod for vec3 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3Index(lua_State *l); - -/** - * Lua __newindex metamethod for vec3 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3NewIndex(lua_State *l); - -/** - * Lua __tostring metamethod for vec3 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec3ToString(lua_State *l); - -/** - * Creates a new vec4 structure in Lua. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4Create(lua_State *l); - -/** - * Lua __index metamethod for vec4 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4Index(lua_State *l); - -/** - * Lua __newindex metamethod for vec4 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4NewIndex(lua_State *l); - -/** - * Lua __tostring metamethod for vec4 structure. - * - * @param l The Lua state. - * @return Number of return values on the Lua stack. - */ -int moduleVec4ToString(lua_State *l); \ No newline at end of file diff --git a/src/dusk/script/module/entity/display/moduleentitycamera.c b/src/dusk/script/module/entity/display/moduleentitycamera.c index 4c1e10d5..942f97a6 100644 --- a/src/dusk/script/module/entity/display/moduleentitycamera.c +++ b/src/dusk/script/module/entity/display/moduleentitycamera.c @@ -9,62 +9,127 @@ #include "assert/assert.h" #include "entity/entity.h" #include "entity/component/display/entitycamera.h" +#include "script/module/math/modulevec2.h" + +typedef struct { + entityid_t entityId; + componentid_t compId; +} entitycamera_handle_t; + +static void pushEntityCameraHandle( + lua_State *L, + entityid_t entityId, + componentid_t compId +) { + entitycamera_handle_t *h = lua_newuserdata( + L, sizeof(entitycamera_handle_t) + ); + h->entityId = entityId; + h->compId = compId; + luaL_getmetatable(L, "entitycamera_mt"); + lua_setmetatable(L, -2); +} + +static int entityCameraIndex(lua_State *L) { + entitycamera_handle_t *h = luaL_checkudata(L, 1, "entitycamera_mt"); + assertNotNull(h, "EntityCamera handle cannot be NULL"); + const char *key = luaL_checkstring(L, 2); + + if(strcmp(key, "zNear") == 0) { + lua_pushnumber( + L, (lua_Number)entityCameraGetZNear(h->entityId, h->compId) + ); + return 1; + } else if(strcmp(key, "zFar") == 0) { + lua_pushnumber( + L, (lua_Number)entityCameraGetZFar(h->entityId, h->compId) + ); + return 1; + } + + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} + +static int entityCameraNewindex(lua_State *L) { + entitycamera_handle_t *h = luaL_checkudata(L, 1, "entitycamera_mt"); + assertNotNull(h, "EntityCamera handle cannot be NULL"); + const char *key = luaL_checkstring(L, 2); + + if(strcmp(key, "zNear") == 0) { + entityCameraSetZNear( + h->entityId, h->compId, (float_t)luaL_checknumber(L, 3) + ); + return 0; + } else if(strcmp(key, "zFar") == 0) { + entityCameraSetZFar( + h->entityId, h->compId, (float_t)luaL_checknumber(L, 3) + ); + return 0; + } + + luaL_error(L, "entitycamera: unknown property '%s'", key); + return 0; +} + +static int entityCameraGetForwardMethod(lua_State *L) { + entitycamera_handle_t *h = luaL_checkudata(L, 1, "entitycamera_mt"); + assertNotNull(h, "EntityCamera handle cannot be NULL"); + vec2 fwd; + entityCameraGetForward(h->entityId, fwd); + luaVec2Push(L, fwd); + return 1; +} + +static int entityCameraGetRightMethod(lua_State *L) { + entitycamera_handle_t *h = luaL_checkudata(L, 1, "entitycamera_mt"); + assertNotNull(h, "EntityCamera handle cannot be NULL"); + vec2 right; + entityCameraGetRight(h->entityId, right); + luaVec2Push(L, right); + return 1; +} + +static int entityCameraAdd(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); + componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_CAMERA); + pushEntityCameraHandle(L, entityId, compId); + return 1; +} + +static int entityCameraGetCurrentGlobal(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + entityid_t camEnts[ENTITY_COUNT_MAX]; + componentid_t camComps[ENTITY_COUNT_MAX]; + entityid_t count = componentGetEntitiesWithComponent( + COMPONENT_TYPE_CAMERA, camEnts, camComps + ); + if(count == 0) { + lua_pushnil(L); + return 1; + } + pushEntityCameraHandle(L, camEnts[0], camComps[0]); + return 1; +} void moduleEntityCamera(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); - #define REG(name, func) lua_register(ctx->luaState, name, func) - REG("entityCameraAdd", moduleEntityCameraAdd); - REG("entityCameraGetZNear", moduleEntityCameraGetZNear); - REG("entityCameraSetZNear", moduleEntityCameraSetZNear); - REG("entityCameraGetZFar", moduleEntityCameraGetZFar); - REG("entityCameraSetZFar", moduleEntityCameraSetZFar); - #undef REG -} - -int moduleEntityCameraAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_CAMERA); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} - -int moduleEntityCameraGetZNear(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - lua_pushnumber(L, (lua_Number)entityCameraGetZNear(entityId, compId)); - return 1; -} - -int moduleEntityCameraSetZNear(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - float_t zNear = (float_t)luaL_checknumber(L, 3); - entityCameraSetZNear(entityId, compId, zNear); - return 0; -} - -int moduleEntityCameraGetZFar(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - lua_pushnumber(L, (lua_Number)entityCameraGetZFar(entityId, compId)); - return 1; -} - -int moduleEntityCameraSetZFar(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - float_t zFar = (float_t)luaL_checknumber(L, 3); - entityCameraSetZFar(entityId, compId, zFar); - return 0; + lua_State *L = ctx->luaState; + + luaL_newmetatable(L, "entitycamera_mt"); + lua_pushcfunction(L, entityCameraIndex); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, entityCameraNewindex); + lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, entityCameraGetForwardMethod); + lua_setfield(L, -2, "getForward"); + lua_pushcfunction(L, entityCameraGetRightMethod); + lua_setfield(L, -2, "getRight"); + lua_pop(L, 1); + + lua_register(L, "entityCameraAdd", entityCameraAdd); + lua_register(L, "entityCameraGetCurrent", entityCameraGetCurrentGlobal); } diff --git a/src/dusk/script/module/entity/display/moduleentitycamera.h b/src/dusk/script/module/entity/display/moduleentitycamera.h index d13223db..f5acfe20 100644 --- a/src/dusk/script/module/entity/display/moduleentitycamera.h +++ b/src/dusk/script/module/entity/display/moduleentitycamera.h @@ -9,9 +9,3 @@ #include "script/scriptcontext.h" void moduleEntityCamera(scriptcontext_t *ctx); - -int moduleEntityCameraAdd(lua_State *L); -int moduleEntityCameraGetZNear(lua_State *L); -int moduleEntityCameraSetZNear(lua_State *L); -int moduleEntityCameraGetZFar(lua_State *L); -int moduleEntityCameraSetZFar(lua_State *L); diff --git a/src/dusk/script/module/entity/display/moduleentitymaterial.c b/src/dusk/script/module/entity/display/moduleentitymaterial.c index 8368b38a..93e54447 100644 --- a/src/dusk/script/module/entity/display/moduleentitymaterial.c +++ b/src/dusk/script/module/entity/display/moduleentitymaterial.c @@ -10,17 +10,65 @@ #include "entity/entity.h" #include "entity/component/display/entitymaterial.h" +typedef struct { + entityid_t entityId; + componentid_t compId; +} entitymaterial_handle_t; + +static void pushEntityMaterialHandle( + lua_State *L, + entityid_t entityId, + componentid_t compId +) { + entitymaterial_handle_t *h = lua_newuserdata( + L, sizeof(entitymaterial_handle_t) + ); + h->entityId = entityId; + h->compId = compId; + luaL_getmetatable(L, "entitymaterial_mt"); + lua_setmetatable(L, -2); +} + +static int entityMaterialIndex(lua_State *L) { + lua_getmetatable(L, 1); + lua_getfield(L, -1, luaL_checkstring(L, 2)); + return 1; +} + +static int entityMaterialNewindex(lua_State *L) { + entitymaterial_handle_t *h = luaL_checkudata(L, 1, "entitymaterial_mt"); + assertNotNull(h, "EntityMaterial handle cannot be NULL"); + const char *key = luaL_checkstring(L, 2); + + if(strcmp(key, "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, "entitymaterial: unknown property '%s'", key); + return 0; +} + +static int entityMaterialAdd(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); + componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_MATERIAL); + pushEntityMaterialHandle(L, entityId, compId); + return 1; +} + void moduleEntityMaterial(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); - lua_register(ctx->luaState, "entityMaterialAdd", moduleEntityMaterialAdd); -} + lua_State *L = ctx->luaState; -int moduleEntityMaterialAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); + luaL_newmetatable(L, "entitymaterial_mt"); + lua_pushcfunction(L, entityMaterialIndex); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, entityMaterialNewindex); + lua_setfield(L, -2, "__newindex"); + lua_pop(L, 1); - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_MATERIAL); - lua_pushnumber(L, (lua_Number)compId); - return 1; + lua_register(L, "entityMaterialAdd", entityMaterialAdd); } diff --git a/src/dusk/script/module/entity/display/moduleentitymaterial.h b/src/dusk/script/module/entity/display/moduleentitymaterial.h index beccb2ad..38fb004c 100644 --- a/src/dusk/script/module/entity/display/moduleentitymaterial.h +++ b/src/dusk/script/module/entity/display/moduleentitymaterial.h @@ -9,5 +9,3 @@ #include "script/scriptcontext.h" void moduleEntityMaterial(scriptcontext_t *ctx); - -int moduleEntityMaterialAdd(lua_State *L); diff --git a/src/dusk/script/module/entity/display/moduleentitymesh.c b/src/dusk/script/module/entity/display/moduleentitymesh.c index ebb4b731..09a2728b 100644 --- a/src/dusk/script/module/entity/display/moduleentitymesh.c +++ b/src/dusk/script/module/entity/display/moduleentitymesh.c @@ -10,17 +10,76 @@ #include "entity/entity.h" #include "entity/component/display/entitymesh.h" +typedef struct { + entityid_t entityId; + componentid_t compId; +} entitymesh_handle_t; + +static void pushEntityMeshHandle( + lua_State *L, + entityid_t entityId, + componentid_t compId +) { + entitymesh_handle_t *h = lua_newuserdata(L, sizeof(entitymesh_handle_t)); + h->entityId = entityId; + h->compId = compId; + luaL_getmetatable(L, "entitymesh_mt"); + lua_setmetatable(L, -2); +} + +static int entityMeshIndex(lua_State *L) { + lua_getmetatable(L, 1); + lua_getfield(L, -1, luaL_checkstring(L, 2)); + return 1; +} + +static int entityMeshGeneratePlaneMethod(lua_State *L) { + entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt"); + assertNotNull(h, "EntityMesh handle cannot be NULL"); + float_t width = (float_t)luaL_checknumber(L, 2); + float_t height = (float_t)luaL_checknumber(L, 3); + errorret_t err = entityMeshGeneratePlane(h->entityId, h->compId, width, height); + if(err.code != ERROR_OK) { + luaL_error(L, "Failed to generate plane mesh"); + } + return 0; +} + +static int entityMeshGenerateCapsuleMethod(lua_State *L) { + entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt"); + assertNotNull(h, "EntityMesh handle cannot be NULL"); + float_t radius = (float_t)luaL_checknumber(L, 2); + float_t halfHeight = (float_t)luaL_checknumber(L, 3); + errorret_t err = entityMeshGenerateCapsule( + h->entityId, h->compId, radius, halfHeight + ); + if(err.code != ERROR_OK) { + luaL_error(L, "Failed to generate capsule mesh"); + } + return 0; +} + +static int entityMeshAdd(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); + componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_MESH); + pushEntityMeshHandle(L, entityId, compId); + return 1; +} + void moduleEntityMesh(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); - lua_register(ctx->luaState, "entityMeshAdd", moduleEntityMeshAdd); -} + lua_State *L = ctx->luaState; -int moduleEntityMeshAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); + luaL_newmetatable(L, "entitymesh_mt"); + lua_pushcfunction(L, entityMeshIndex); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, entityMeshGeneratePlaneMethod); + lua_setfield(L, -2, "generatePlane"); + lua_pushcfunction(L, entityMeshGenerateCapsuleMethod); + lua_setfield(L, -2, "generateCapsule"); + lua_pop(L, 1); - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_MESH); - lua_pushnumber(L, (lua_Number)compId); - return 1; + lua_register(L, "entityMeshAdd", entityMeshAdd); } diff --git a/src/dusk/script/module/entity/display/moduleentitymesh.h b/src/dusk/script/module/entity/display/moduleentitymesh.h index d333c90a..0b8ab3d0 100644 --- a/src/dusk/script/module/entity/display/moduleentitymesh.h +++ b/src/dusk/script/module/entity/display/moduleentitymesh.h @@ -9,5 +9,3 @@ #include "script/scriptcontext.h" void moduleEntityMesh(scriptcontext_t *ctx); - -int moduleEntityMeshAdd(lua_State *L); diff --git a/src/dusk/script/module/entity/display/moduleentityposition.c b/src/dusk/script/module/entity/display/moduleentityposition.c index 0bbdee6d..bc406e96 100644 --- a/src/dusk/script/module/entity/display/moduleentityposition.c +++ b/src/dusk/script/module/entity/display/moduleentityposition.c @@ -9,132 +9,145 @@ #include "assert/assert.h" #include "entity/entity.h" #include "entity/component/display/entityposition.h" +#include "script/module/math/modulevec3.h" + +typedef struct { + entityid_t entityId; + componentid_t compId; +} entityposition_handle_t; + +static void pushEntityPositionHandle( + lua_State *L, + entityid_t entityId, + componentid_t compId +) { + entityposition_handle_t *h = lua_newuserdata( + L, sizeof(entityposition_handle_t) + ); + h->entityId = entityId; + h->compId = compId; + luaL_getmetatable(L, "entityposition_mt"); + lua_setmetatable(L, -2); +} + +static int entityPositionIndex(lua_State *L) { + entityposition_handle_t *h = luaL_checkudata(L, 1, "entityposition_mt"); + assertNotNull(h, "EntityPosition handle cannot be NULL"); + const char *key = luaL_checkstring(L, 2); + + if(strcmp(key, "x") == 0) { + vec3 v; entityPositionGetPosition(h->entityId, h->compId, v); + lua_pushnumber(L, v[0]); return 1; + } else if(strcmp(key, "y") == 0) { + vec3 v; entityPositionGetPosition(h->entityId, h->compId, v); + lua_pushnumber(L, v[1]); return 1; + } else if(strcmp(key, "z") == 0) { + vec3 v; entityPositionGetPosition(h->entityId, h->compId, v); + lua_pushnumber(L, v[2]); return 1; + } else if(strcmp(key, "rotX") == 0) { + vec3 v; entityPositionGetRotation(h->entityId, h->compId, v); + lua_pushnumber(L, v[0]); return 1; + } else if(strcmp(key, "rotY") == 0) { + vec3 v; entityPositionGetRotation(h->entityId, h->compId, v); + lua_pushnumber(L, v[1]); return 1; + } else if(strcmp(key, "rotZ") == 0) { + vec3 v; entityPositionGetRotation(h->entityId, h->compId, v); + lua_pushnumber(L, v[2]); return 1; + } else if(strcmp(key, "scaleX") == 0) { + vec3 v; entityPositionGetScale(h->entityId, h->compId, v); + lua_pushnumber(L, v[0]); return 1; + } else if(strcmp(key, "scaleY") == 0) { + vec3 v; entityPositionGetScale(h->entityId, h->compId, v); + lua_pushnumber(L, v[1]); return 1; + } else if(strcmp(key, "scaleZ") == 0) { + vec3 v; entityPositionGetScale(h->entityId, h->compId, v); + lua_pushnumber(L, v[2]); return 1; + } + + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} + +static int entityPositionNewindex(lua_State *L) { + entityposition_handle_t *h = luaL_checkudata(L, 1, "entityposition_mt"); + assertNotNull(h, "EntityPosition handle cannot be NULL"); + const char *key = luaL_checkstring(L, 2); + + if(strcmp(key, "x") == 0) { + vec3 v; entityPositionGetPosition(h->entityId, h->compId, v); + v[0] = (float_t)luaL_checknumber(L, 3); + entityPositionSetPosition(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "y") == 0) { + vec3 v; entityPositionGetPosition(h->entityId, h->compId, v); + v[1] = (float_t)luaL_checknumber(L, 3); + entityPositionSetPosition(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "z") == 0) { + vec3 v; entityPositionGetPosition(h->entityId, h->compId, v); + v[2] = (float_t)luaL_checknumber(L, 3); + entityPositionSetPosition(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "rotX") == 0) { + vec3 v; entityPositionGetRotation(h->entityId, h->compId, v); + v[0] = (float_t)luaL_checknumber(L, 3); + entityPositionSetRotation(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "rotY") == 0) { + vec3 v; entityPositionGetRotation(h->entityId, h->compId, v); + v[1] = (float_t)luaL_checknumber(L, 3); + entityPositionSetRotation(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "rotZ") == 0) { + vec3 v; entityPositionGetRotation(h->entityId, h->compId, v); + v[2] = (float_t)luaL_checknumber(L, 3); + entityPositionSetRotation(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "scaleX") == 0) { + vec3 v; entityPositionGetScale(h->entityId, h->compId, v); + v[0] = (float_t)luaL_checknumber(L, 3); + entityPositionSetScale(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "scaleY") == 0) { + vec3 v; entityPositionGetScale(h->entityId, h->compId, v); + v[1] = (float_t)luaL_checknumber(L, 3); + entityPositionSetScale(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "scaleZ") == 0) { + vec3 v; 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, "entityposition: unknown property '%s'", key); + return 0; +} + +static int entityPositionLookAtMethod(lua_State *L) { + entityposition_handle_t *h = luaL_checkudata(L, 1, "entityposition_mt"); + assertNotNull(h, "EntityPosition handle cannot be NULL"); + vec3 target, up, eye; + luaVec3Check(L, 2, target); + luaVec3Check(L, 3, up); + luaVec3Check(L, 4, eye); + entityPositionLookAt(h->entityId, h->compId, target, up, eye); + return 0; +} + +static int entityPositionAdd(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); + componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_POSITION); + pushEntityPositionHandle(L, entityId, compId); + return 1; +} void moduleEntityPosition(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); - #define REG(name, func) lua_register(ctx->luaState, name, func) - REG("entityPositionAdd", moduleEntityPositionAdd); - REG("entityPositionSetPosition", moduleEntityPositionSetPosition); - REG("entityPositionGetPosition", moduleEntityPositionGetPosition); - REG("entityPositionSetRotation", moduleEntityPositionSetRotation); - REG("entityPositionGetRotation", moduleEntityPositionGetRotation); - REG("entityPositionSetScale", moduleEntityPositionSetScale); - REG("entityPositionGetScale", moduleEntityPositionGetScale); - REG("entityPositionLookAt", moduleEntityPositionLookAt); - #undef REG -} - -int moduleEntityPositionAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_POSITION); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} - -int moduleEntityPositionSetPosition(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 pos = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPositionSetPosition(entityId, compId, pos); - return 0; -} - -int moduleEntityPositionGetPosition(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 pos; - entityPositionGetPosition(entityId, compId, pos); - lua_pushnumber(L, pos[0]); - lua_pushnumber(L, pos[1]); - lua_pushnumber(L, pos[2]); - return 3; -} - -int moduleEntityPositionSetRotation(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 rot = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPositionSetRotation(entityId, compId, rot); - return 0; -} - -int moduleEntityPositionGetRotation(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 rot; - entityPositionGetRotation(entityId, compId, rot); - lua_pushnumber(L, rot[0]); - lua_pushnumber(L, rot[1]); - lua_pushnumber(L, rot[2]); - return 3; -} - -int moduleEntityPositionSetScale(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 scale = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPositionSetScale(entityId, compId, scale); - return 0; -} - -int moduleEntityPositionGetScale(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 scale; - entityPositionGetScale(entityId, compId, scale); - lua_pushnumber(L, scale[0]); - lua_pushnumber(L, scale[1]); - lua_pushnumber(L, scale[2]); - return 3; -} - -int moduleEntityPositionLookAt(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 target = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - vec3 up = { - (float_t)luaL_checknumber(L, 6), - (float_t)luaL_checknumber(L, 7), - (float_t)luaL_checknumber(L, 8) - }; - vec3 eye = { - (float_t)luaL_checknumber(L, 9), - (float_t)luaL_checknumber(L, 10), - (float_t)luaL_checknumber(L, 11) - }; - entityPositionLookAt(entityId, compId, target, up, eye); - return 0; + lua_State *L = ctx->luaState; + + luaL_newmetatable(L, "entityposition_mt"); + lua_pushcfunction(L, entityPositionIndex); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, entityPositionNewindex); + lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, entityPositionLookAtMethod); + lua_setfield(L, -2, "lookAt"); + lua_pop(L, 1); + + lua_register(L, "entityPositionAdd", entityPositionAdd); } diff --git a/src/dusk/script/module/entity/display/moduleentityposition.h b/src/dusk/script/module/entity/display/moduleentityposition.h index 0ab13d41..6097ac0e 100644 --- a/src/dusk/script/module/entity/display/moduleentityposition.h +++ b/src/dusk/script/module/entity/display/moduleentityposition.h @@ -9,12 +9,3 @@ #include "script/scriptcontext.h" void moduleEntityPosition(scriptcontext_t *ctx); - -int moduleEntityPositionAdd(lua_State *L); -int moduleEntityPositionSetPosition(lua_State *L); -int moduleEntityPositionGetPosition(lua_State *L); -int moduleEntityPositionSetRotation(lua_State *L); -int moduleEntityPositionGetRotation(lua_State *L); -int moduleEntityPositionSetScale(lua_State *L); -int moduleEntityPositionGetScale(lua_State *L); -int moduleEntityPositionLookAt(lua_State *L); diff --git a/src/dusk/script/module/entity/physics/moduleentityphysics.c b/src/dusk/script/module/entity/physics/moduleentityphysics.c index 00947360..6a9a0016 100644 --- a/src/dusk/script/module/entity/physics/moduleentityphysics.c +++ b/src/dusk/script/module/entity/physics/moduleentityphysics.c @@ -9,132 +9,187 @@ #include "assert/assert.h" #include "entity/entity.h" #include "entity/component/physics/entityphysics.h" +#include "physics/physicsbodytype.h" +#include "script/module/math/modulevec3.h" + +typedef struct { + entityid_t entityId; + componentid_t compId; +} entityphysics_handle_t; + +static void pushEntityPhysicsHandle( + lua_State *L, + entityid_t entityId, + componentid_t compId +) { + entityphysics_handle_t *h = lua_newuserdata( + L, sizeof(entityphysics_handle_t) + ); + h->entityId = entityId; + h->compId = compId; + luaL_getmetatable(L, "entityphysics_mt"); + lua_setmetatable(L, -2); +} + +static int entityPhysicsIndex(lua_State *L) { + entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); + assertNotNull(h, "EntityPhysics handle cannot be NULL"); + const char *key = luaL_checkstring(L, 2); + + if(strcmp(key, "bodyType") == 0) { + lua_pushnumber( + L, (lua_Number)entityPhysicsGetBodyType(h->entityId, h->compId) + ); + return 1; + } else if(strcmp(key, "velocityX") == 0) { + vec3 v; entityPhysicsGetVelocity(h->entityId, h->compId, v); + lua_pushnumber(L, v[0]); return 1; + } else if(strcmp(key, "velocityY") == 0) { + vec3 v; entityPhysicsGetVelocity(h->entityId, h->compId, v); + lua_pushnumber(L, v[1]); return 1; + } else if(strcmp(key, "velocityZ") == 0) { + vec3 v; entityPhysicsGetVelocity(h->entityId, h->compId, v); + lua_pushnumber(L, v[2]); return 1; + } else if(strcmp(key, "velocity") == 0) { + vec3 v; entityPhysicsGetVelocity(h->entityId, h->compId, v); + luaVec3Push(L, v); return 1; + } else if(strcmp(key, "onGround") == 0) { + lua_pushboolean( + L, (int)entityPhysicsIsOnGround(h->entityId, h->compId) + ); + return 1; + } + + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} + +static int entityPhysicsNewindex(lua_State *L) { + entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); + assertNotNull(h, "EntityPhysics handle cannot be NULL"); + const char *key = luaL_checkstring(L, 2); + + if(strcmp(key, "bodyType") == 0) { + physicsbodytype_t t = (physicsbodytype_t)luaL_checknumber(L, 3); + entityPhysicsSetBodyType(h->entityId, h->compId, t); + return 0; + } else if(strcmp(key, "velocityX") == 0) { + vec3 v; entityPhysicsGetVelocity(h->entityId, h->compId, v); + v[0] = (float_t)luaL_checknumber(L, 3); + entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "velocityY") == 0) { + vec3 v; entityPhysicsGetVelocity(h->entityId, h->compId, v); + v[1] = (float_t)luaL_checknumber(L, 3); + entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "velocityZ") == 0) { + vec3 v; entityPhysicsGetVelocity(h->entityId, h->compId, v); + v[2] = (float_t)luaL_checknumber(L, 3); + entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0; + } else if(strcmp(key, "velocity") == 0) { + vec3 v; luaVec3Check(L, 3, v); + entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0; + } + + luaL_error(L, "entityphysics: unknown property '%s'", key); + return 0; +} + +static int entityPhysicsSetShapeCapsuleMethod(lua_State *L) { + entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); + assertNotNull(h, "EntityPhysics handle cannot be NULL"); + 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; +} + +static int entityPhysicsSetShapePlaneMethod(lua_State *L) { + entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); + assertNotNull(h, "EntityPhysics handle cannot be NULL"); + 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; +} + +static int entityPhysicsSetShapeSphereMethod(lua_State *L) { + entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); + assertNotNull(h, "EntityPhysics handle cannot be NULL"); + 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; +} + +static int entityPhysicsSetShapeCubeMethod(lua_State *L) { + entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); + assertNotNull(h, "EntityPhysics handle cannot be NULL"); + 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; +} + +static int entityPhysicsApplyImpulseMethod(lua_State *L) { + entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); + assertNotNull(h, "EntityPhysics handle cannot be NULL"); + 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; +} + +static int entityPhysicsAdd(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); + componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_PHYSICS); + pushEntityPhysicsHandle(L, entityId, compId); + return 1; +} void moduleEntityPhysics(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); - #define REG(name, func) lua_register(ctx->luaState, name, func) - REG("entityPhysicsAdd", moduleEntityPhysicsAdd); - REG("entityPhysicsSetVelocity", moduleEntityPhysicsSetVelocity); - REG("entityPhysicsGetVelocity", moduleEntityPhysicsGetVelocity); - REG("entityPhysicsApplyImpulse", moduleEntityPhysicsApplyImpulse); - REG("entityPhysicsIsOnGround", moduleEntityPhysicsIsOnGround); - REG("entityPhysicsSetShapeCube", moduleEntityPhysicsSetShapeCube); - REG("entityPhysicsSetShapeSphere", moduleEntityPhysicsSetShapeSphere); - REG("entityPhysicsSetShapeCapsule", moduleEntityPhysicsSetShapeCapsule); - REG("entityPhysicsSetShapePlane", moduleEntityPhysicsSetShapePlane); - #undef REG -} - -int moduleEntityPhysicsAdd(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = entityAddComponent(entityId, COMPONENT_TYPE_PHYSICS); - lua_pushnumber(L, (lua_Number)compId); - return 1; -} - -int moduleEntityPhysicsSetVelocity(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 vel = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPhysicsSetVelocity(entityId, compId, vel); - return 0; -} - -int moduleEntityPhysicsGetVelocity(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 vel; - entityPhysicsGetVelocity(entityId, compId, vel); - lua_pushnumber(L, vel[0]); - lua_pushnumber(L, vel[1]); - lua_pushnumber(L, vel[2]); - return 3; -} - -int moduleEntityPhysicsApplyImpulse(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - vec3 impulse = { - (float_t)luaL_checknumber(L, 3), - (float_t)luaL_checknumber(L, 4), - (float_t)luaL_checknumber(L, 5) - }; - entityPhysicsApplyImpulse(entityId, compId, impulse); - return 0; -} - -int moduleEntityPhysicsIsOnGround(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - lua_pushboolean(L, (int)entityPhysicsIsOnGround(entityId, compId)); - return 1; -} - -int moduleEntityPhysicsSetShapeCube(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_CUBE; - shape.data.cube.halfExtents[0] = (float_t)luaL_checknumber(L, 3); - shape.data.cube.halfExtents[1] = (float_t)luaL_checknumber(L, 4); - shape.data.cube.halfExtents[2] = (float_t)luaL_checknumber(L, 5); - entityPhysicsSetShape(entityId, compId, shape); - return 0; -} - -int moduleEntityPhysicsSetShapeSphere(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_SPHERE; - shape.data.sphere.radius = (float_t)luaL_checknumber(L, 3); - entityPhysicsSetShape(entityId, compId, shape); - return 0; -} - -int moduleEntityPhysicsSetShapeCapsule(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_CAPSULE; - shape.data.capsule.radius = (float_t)luaL_checknumber(L, 3); - shape.data.capsule.halfHeight = (float_t)luaL_checknumber(L, 4); - entityPhysicsSetShape(entityId, compId, shape); - return 0; -} - -int moduleEntityPhysicsSetShapePlane(lua_State *L) { - assertNotNull(L, "Lua state cannot be NULL"); - - entityid_t entityId = (entityid_t)luaL_checknumber(L, 1); - componentid_t compId = (componentid_t)luaL_checknumber(L, 2); - physicsshape_t shape; - shape.type = PHYSICS_SHAPE_PLANE; - shape.data.plane.normal[0] = (float_t)luaL_checknumber(L, 3); - shape.data.plane.normal[1] = (float_t)luaL_checknumber(L, 4); - shape.data.plane.normal[2] = (float_t)luaL_checknumber(L, 5); - shape.data.plane.distance = (float_t)luaL_checknumber(L, 6); - entityPhysicsSetShape(entityId, compId, shape); - return 0; + lua_State *L = ctx->luaState; + + #define SET(name, val) \ + lua_pushnumber(L, (lua_Number)(val)); \ + lua_setglobal(L, name) + SET("PHYSICS_BODY_STATIC", PHYSICS_BODY_STATIC); + SET("PHYSICS_BODY_DYNAMIC", PHYSICS_BODY_DYNAMIC); + SET("PHYSICS_BODY_KINEMATIC", PHYSICS_BODY_KINEMATIC); + #undef SET + + luaL_newmetatable(L, "entityphysics_mt"); + lua_pushcfunction(L, entityPhysicsIndex); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, entityPhysicsNewindex); + lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, entityPhysicsSetShapeCapsuleMethod); + lua_setfield(L, -2, "setShapeCapsule"); + lua_pushcfunction(L, entityPhysicsSetShapePlaneMethod); + lua_setfield(L, -2, "setShapePlane"); + lua_pushcfunction(L, entityPhysicsSetShapeSphereMethod); + lua_setfield(L, -2, "setShapeSphere"); + lua_pushcfunction(L, entityPhysicsSetShapeCubeMethod); + lua_setfield(L, -2, "setShapeCube"); + lua_pushcfunction(L, entityPhysicsApplyImpulseMethod); + lua_setfield(L, -2, "applyImpulse"); + lua_pop(L, 1); + + lua_register(L, "entityPhysicsAdd", entityPhysicsAdd); } diff --git a/src/dusk/script/module/entity/physics/moduleentityphysics.h b/src/dusk/script/module/entity/physics/moduleentityphysics.h index 93175881..30e967b9 100644 --- a/src/dusk/script/module/entity/physics/moduleentityphysics.h +++ b/src/dusk/script/module/entity/physics/moduleentityphysics.h @@ -9,13 +9,3 @@ #include "script/scriptcontext.h" void moduleEntityPhysics(scriptcontext_t *ctx); - -int moduleEntityPhysicsAdd(lua_State *L); -int moduleEntityPhysicsSetVelocity(lua_State *L); -int moduleEntityPhysicsGetVelocity(lua_State *L); -int moduleEntityPhysicsApplyImpulse(lua_State *L); -int moduleEntityPhysicsIsOnGround(lua_State *L); -int moduleEntityPhysicsSetShapeCube(lua_State *L); -int moduleEntityPhysicsSetShapeSphere(lua_State *L); -int moduleEntityPhysicsSetShapeCapsule(lua_State *L); -int moduleEntityPhysicsSetShapePlane(lua_State *L); diff --git a/src/dusk/script/module/event/moduleevent.c b/src/dusk/script/module/event/moduleevent.c index db4186aa..ef72cf2e 100644 --- a/src/dusk/script/module/event/moduleevent.c +++ b/src/dusk/script/module/event/moduleevent.c @@ -39,7 +39,26 @@ int moduleEventSubscribe(lua_State *L) { return 1; } +int moduleEventUnsubscribe(lua_State *L) { + assertNotNull(L, "Lua state cannot be NULL"); + + if(!lua_islightuserdata(L, 1)) { + luaL_error(L, "eventUnsubscribe: Expected event pointer as first argument"); + return 0; + } + if(!lua_isnumber(L, 2)) { + luaL_error(L, "eventUnsubscribe: Expected subscription ID as second argument"); + return 0; + } + + event_t *event = (event_t *)lua_touserdata(L, 1); + assertNotNull(event, "Event cannot be NULL"); + eventsub_t id = (eventsub_t)lua_tonumber(L, 2); + eventUnsubscribe(event, id); + return 0; +} + void moduleEvent(scriptcontext_t *context) { - // Reg functions lua_register(context->luaState, "eventSubscribe", moduleEventSubscribe); + lua_register(context->luaState, "eventUnsubscribe", moduleEventUnsubscribe); } \ No newline at end of file diff --git a/src/dusk/script/module/event/moduleevent.h b/src/dusk/script/module/event/moduleevent.h index c7c34f0b..dfdc805f 100644 --- a/src/dusk/script/module/event/moduleevent.h +++ b/src/dusk/script/module/event/moduleevent.h @@ -18,4 +18,9 @@ void moduleEvent(scriptcontext_t *context); /** * Script binding for subscribing to an event. */ -int moduleEventSubscribe(lua_State *L); \ No newline at end of file +int moduleEventSubscribe(lua_State *L); + +/** + * Script binding for unsubscribing from an event. + */ +int moduleEventUnsubscribe(lua_State *L); \ No newline at end of file diff --git a/src/dusk/script/module/math/CMakeLists.txt b/src/dusk/script/module/math/CMakeLists.txt new file mode 100644 index 00000000..53d97b02 --- /dev/null +++ b/src/dusk/script/module/math/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2026 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +target_sources(${DUSK_LIBRARY_TARGET_NAME} + PUBLIC + modulevec2.c + modulevec3.c + modulevec4.c + modulemat4.c + modulemath.c +) diff --git a/src/dusk/script/module/math/modulemat4.c b/src/dusk/script/module/math/modulemat4.c new file mode 100644 index 00000000..48e3cd48 --- /dev/null +++ b/src/dusk/script/module/math/modulemat4.c @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "modulemat4.h" +#include "modulevec3.h" +#include "modulevec4.h" +#include "assert/assert.h" + +void luaMat4Push(lua_State *L, mat4 m) { + mat4 *u = (mat4 *)lua_newuserdata(L, sizeof(mat4)); + glm_mat4_copy(m, *u); + luaL_getmetatable(L, "mat4_mt"); + lua_setmetatable(L, -2); +} + +void luaMat4Check(lua_State *L, int idx, mat4 out) { + mat4 *m = (mat4 *)luaL_checkudata(L, idx, "mat4_mt"); + glm_mat4_copy(*m, out); +} + +static int mat4Index(lua_State *L) { + lua_getmetatable(L, 1); + lua_getfield(L, -1, luaL_checkstring(L, 2)); + return 1; +} + +static int mat4OpMul(lua_State *L) { + mat4 *a = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + mat4 *b = (mat4 *)luaL_checkudata(L, 2, "mat4_mt"); + mat4 r; glm_mat4_mul(*a, *b, r); + luaMat4Push(L, r); return 1; +} + +static int mat4OpToString(lua_State *L) { + lua_pushstring(L, "mat4(...)"); return 1; +} + +static int mat4Transpose(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + mat4 r; glm_mat4_transpose_to(*m, r); + luaMat4Push(L, r); return 1; +} + +static int mat4Inverse(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + mat4 r; glm_mat4_inv(*m, r); + luaMat4Push(L, r); return 1; +} + +static int mat4MulVec3(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + vec3 v; luaVec3Check(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; +} + +static int mat4MulVec4(lua_State *L) { + 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; +} + +static int mat4Translate(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + vec3 v; luaVec3Check(L, 2, v); + mat4 r; glm_mat4_copy(*m, r); + glm_translate(r, v); + luaMat4Push(L, r); return 1; +} + +static int mat4Scale(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + vec3 v; luaVec3Check(L, 2, v); + mat4 r; glm_mat4_copy(*m, r); + glm_scale(r, v); + luaMat4Push(L, r); return 1; +} + +static int mat4Identity(lua_State *L) { + mat4 r; glm_mat4_identity(r); + luaMat4Push(L, r); return 1; +} + +static int mat4Determinant(lua_State *L) { + mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt"); + 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; +} + +void moduleMathMat4(lua_State *L) { + if(!luaL_newmetatable(L, "mat4_mt")) { lua_pop(L, 1); return; } + + 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"); + lua_pop(L, 1); + + lua_register(L, "mat4", mat4Create); +} diff --git a/src/dusk/script/module/math/modulemat4.h b/src/dusk/script/module/math/modulemat4.h new file mode 100644 index 00000000..5d5bf591 --- /dev/null +++ b/src/dusk/script/module/math/modulemat4.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/scriptcontext.h" + +void luaMat4Push(lua_State *L, mat4 m); +void luaMat4Check(lua_State *L, int idx, mat4 out); +void moduleMathMat4(lua_State *L); diff --git a/src/dusk/script/module/math/modulemath.c b/src/dusk/script/module/math/modulemath.c new file mode 100644 index 00000000..ff198ecb --- /dev/null +++ b/src/dusk/script/module/math/modulemath.c @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "modulemath.h" +#include "modulevec2.h" +#include "modulevec3.h" +#include "modulevec4.h" +#include "modulemat4.h" +#include "assert/assert.h" + +void moduleMath(scriptcontext_t *ctx) { + assertNotNull(ctx, "Script context cannot be NULL"); + lua_State *L = ctx->luaState; + moduleMathVec2(L); + moduleMathVec3(L); + moduleMathVec4(L); + moduleMathMat4(L); +} diff --git a/src/dusk/script/module/math/modulemath.h b/src/dusk/script/module/math/modulemath.h new file mode 100644 index 00000000..5cf31a3b --- /dev/null +++ b/src/dusk/script/module/math/modulemath.h @@ -0,0 +1,11 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/scriptcontext.h" + +void moduleMath(scriptcontext_t *ctx); diff --git a/src/dusk/script/module/math/modulevec2.c b/src/dusk/script/module/math/modulevec2.c new file mode 100644 index 00000000..0c542ff1 --- /dev/null +++ b/src/dusk/script/module/math/modulevec2.c @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "modulevec2.h" +#include "assert/assert.h" + +void luaVec2Push(lua_State *L, vec2 v) { + vec2 *u = (vec2 *)lua_newuserdata(L, sizeof(vec2)); + glm_vec2_copy(v, *u); + luaL_getmetatable(L, "vec2_mt"); + lua_setmetatable(L, -2); +} + +void luaVec2Check(lua_State *L, int idx, vec2 out) { + vec2 *v = (vec2 *)luaL_checkudata(L, idx, "vec2_mt"); + glm_vec2_copy(*v, out); +} + +static int vec2Index(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + const char *key = luaL_checkstring(L, 2); + if(strcmp(key, "x") == 0) { lua_pushnumber(L, (lua_Number)(*v)[0]); return 1; } + if(strcmp(key, "y") == 0) { lua_pushnumber(L, (lua_Number)(*v)[1]); return 1; } + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} + +static int vec2Newindex(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + const char *key = luaL_checkstring(L, 2); + if(strcmp(key, "x") == 0) { (*v)[0] = (float_t)luaL_checknumber(L, 3); return 0; } + if(strcmp(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) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + vec2 r; glm_vec2_add(*a, *b, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpSub(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + vec2 r; glm_vec2_sub(*a, *b, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpMul(lua_State *L) { + 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"); + glm_vec2_scale(*v, s, r); + } else { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + glm_vec2_scale(*v, s, r); + } + luaVec2Push(L, r); return 1; +} + +static int vec2OpDiv(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + vec2 r; glm_vec2_divs(*v, s, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpUnm(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 r; glm_vec2_negate_to(*v, r); + luaVec2Push(L, r); return 1; +} + +static int vec2OpEq(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1]); + return 1; +} + +static int vec2OpToString(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + char buf[64]; + snprintf(buf, sizeof(buf), "vec2(%.3f, %.3f)", (*v)[0], (*v)[1]); + lua_pushstring(L, buf); return 1; +} + +static int vec2Dot(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_dot(*a, *b)); return 1; +} + +static int vec2Length(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_norm(*v)); return 1; +} + +static int vec2LengthSq(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_norm2(*v)); return 1; +} + +static int vec2Normalize(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 r; glm_vec2_normalize_to(*v, r); + luaVec2Push(L, r); return 1; +} + +static int vec2Lerp(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + float_t t = (float_t)luaL_checknumber(L, 3); + vec2 r; glm_vec2_lerp(*a, *b, t, r); + luaVec2Push(L, r); return 1; +} + +static int vec2Distance(lua_State *L) { + vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); + lua_pushnumber(L, (lua_Number)glm_vec2_distance(*a, *b)); return 1; +} + +static int vec2Negate(lua_State *L) { + vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); + vec2 r; glm_vec2_negate_to(*v, r); + luaVec2Push(L, r); return 1; +} + +static int vec2Create(lua_State *L) { + 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; +} + +void moduleMathVec2(lua_State *L) { + if(!luaL_newmetatable(L, "vec2_mt")) { lua_pop(L, 1); return; } + + 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"); + lua_pop(L, 1); + + lua_register(L, "vec2", vec2Create); +} diff --git a/src/dusk/script/module/math/modulevec2.h b/src/dusk/script/module/math/modulevec2.h new file mode 100644 index 00000000..03c1431b --- /dev/null +++ b/src/dusk/script/module/math/modulevec2.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/scriptcontext.h" + +void luaVec2Push(lua_State *L, vec2 v); +void luaVec2Check(lua_State *L, int idx, vec2 out); +void moduleMathVec2(lua_State *L); diff --git a/src/dusk/script/module/math/modulevec3.c b/src/dusk/script/module/math/modulevec3.c new file mode 100644 index 00000000..ef23b400 --- /dev/null +++ b/src/dusk/script/module/math/modulevec3.c @@ -0,0 +1,180 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "modulevec3.h" +#include "assert/assert.h" + +void luaVec3Push(lua_State *L, vec3 v) { + vec3 *u = (vec3 *)lua_newuserdata(L, sizeof(vec3)); + glm_vec3_copy(v, *u); + luaL_getmetatable(L, "vec3_mt"); + lua_setmetatable(L, -2); +} + +void luaVec3Check(lua_State *L, int idx, vec3 out) { + vec3 *v = (vec3 *)luaL_checkudata(L, idx, "vec3_mt"); + glm_vec3_copy(*v, out); +} + +static int vec3Index(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + const char *key = luaL_checkstring(L, 2); + if(strcmp(key, "x") == 0) { lua_pushnumber(L, (lua_Number)(*v)[0]); return 1; } + if(strcmp(key, "y") == 0) { lua_pushnumber(L, (lua_Number)(*v)[1]); return 1; } + if(strcmp(key, "z") == 0) { lua_pushnumber(L, (lua_Number)(*v)[2]); return 1; } + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} + +static int vec3Newindex(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + const char *key = luaL_checkstring(L, 2); + if(strcmp(key, "x") == 0) { (*v)[0] = (float_t)luaL_checknumber(L, 3); return 0; } + if(strcmp(key, "y") == 0) { (*v)[1] = (float_t)luaL_checknumber(L, 3); return 0; } + if(strcmp(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) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + vec3 r; glm_vec3_add(*a, *b, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpSub(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + vec3 r; glm_vec3_sub(*a, *b, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpMul(lua_State *L) { + 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"); + glm_vec3_scale(*v, s, r); + } else { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + glm_vec3_scale(*v, s, r); + } + luaVec3Push(L, r); return 1; +} + +static int vec3OpDiv(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + float_t s = (float_t)luaL_checknumber(L, 2); + vec3 r; glm_vec3_divs(*v, s, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpUnm(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 r; glm_vec3_negate_to(*v, r); + luaVec3Push(L, r); return 1; +} + +static int vec3OpEq(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] && (*a)[2] == (*b)[2]); + return 1; +} + +static int vec3OpToString(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + char buf[80]; + snprintf(buf, sizeof(buf), "vec3(%.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2]); + lua_pushstring(L, buf); return 1; +} + +static int vec3Dot(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_dot(*a, *b)); return 1; +} + +static int vec3Cross(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + vec3 r; glm_vec3_cross(*a, *b, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Length(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_norm(*v)); return 1; +} + +static int vec3LengthSq(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_norm2(*v)); return 1; +} + +static int vec3Normalize(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 r; glm_vec3_normalize_to(*v, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Lerp(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + float_t t = (float_t)luaL_checknumber(L, 3); + vec3 r; glm_vec3_lerp(*a, *b, t, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Distance(lua_State *L) { + vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); + lua_pushnumber(L, (lua_Number)glm_vec3_distance(*a, *b)); return 1; +} + +static int vec3Negate(lua_State *L) { + vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); + vec3 r; glm_vec3_negate_to(*v, r); + luaVec3Push(L, r); return 1; +} + +static int vec3Create(lua_State *L) { + 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; +} + +void moduleMathVec3(lua_State *L) { + if(!luaL_newmetatable(L, "vec3_mt")) { lua_pop(L, 1); return; } + + 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"); + lua_pop(L, 1); + + lua_register(L, "vec3", vec3Create); +} diff --git a/src/dusk/script/module/math/modulevec3.h b/src/dusk/script/module/math/modulevec3.h new file mode 100644 index 00000000..b7cef869 --- /dev/null +++ b/src/dusk/script/module/math/modulevec3.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/scriptcontext.h" + +void luaVec3Push(lua_State *L, vec3 v); +void luaVec3Check(lua_State *L, int idx, vec3 out); +void moduleMathVec3(lua_State *L); diff --git a/src/dusk/script/module/math/modulevec4.c b/src/dusk/script/module/math/modulevec4.c new file mode 100644 index 00000000..ed076593 --- /dev/null +++ b/src/dusk/script/module/math/modulevec4.c @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "modulevec4.h" +#include "assert/assert.h" + +void luaVec4Push(lua_State *L, vec4 v) { + vec4 *u = (vec4 *)lua_newuserdata(L, sizeof(vec4)); + glm_vec4_copy(v, *u); + luaL_getmetatable(L, "vec4_mt"); + lua_setmetatable(L, -2); +} + +void luaVec4Check(lua_State *L, int idx, vec4 out) { + vec4 *v = (vec4 *)luaL_checkudata(L, idx, "vec4_mt"); + glm_vec4_copy(*v, out); +} + +static int vec4Index(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + const char *key = luaL_checkstring(L, 2); + if(strcmp(key, "x") == 0 || strcmp(key, "u0") == 0) { lua_pushnumber(L, (lua_Number)(*v)[0]); return 1; } + if(strcmp(key, "y") == 0 || strcmp(key, "v0") == 0) { lua_pushnumber(L, (lua_Number)(*v)[1]); return 1; } + if(strcmp(key, "z") == 0 || strcmp(key, "u1") == 0) { lua_pushnumber(L, (lua_Number)(*v)[2]); return 1; } + if(strcmp(key, "w") == 0 || strcmp(key, "v1") == 0) { lua_pushnumber(L, (lua_Number)(*v)[3]); return 1; } + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + return 1; +} + +static int vec4Newindex(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + const char *key = luaL_checkstring(L, 2); + if(strcmp(key, "x") == 0 || strcmp(key, "u0") == 0) { (*v)[0] = (float_t)luaL_checknumber(L, 3); return 0; } + if(strcmp(key, "y") == 0 || strcmp(key, "v0") == 0) { (*v)[1] = (float_t)luaL_checknumber(L, 3); return 0; } + if(strcmp(key, "z") == 0 || strcmp(key, "u1") == 0) { (*v)[2] = (float_t)luaL_checknumber(L, 3); return 0; } + if(strcmp(key, "w") == 0 || strcmp(key, "v1") == 0) { (*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) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + vec4 r; glm_vec4_add(*a, *b, r); + luaVec4Push(L, r); return 1; +} + +static int vec4OpSub(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + vec4 r; glm_vec4_sub(*a, *b, r); + luaVec4Push(L, r); return 1; +} + +static int vec4OpMul(lua_State *L) { + 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"); + glm_vec4_scale(*v, s, r); + } else { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + 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]); + return 1; +} + +static int vec4OpToString(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + 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; +} + +static int vec4Dot(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + lua_pushnumber(L, (lua_Number)glm_vec4_dot(*a, *b)); return 1; +} + +static int vec4Length(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + lua_pushnumber(L, (lua_Number)glm_vec4_norm(*v)); return 1; +} + +static int vec4LengthSq(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + lua_pushnumber(L, (lua_Number)glm_vec4_norm2(*v)); return 1; +} + +static int vec4Normalize(lua_State *L) { + vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 r; glm_vec4_normalize_to(*v, r); + luaVec4Push(L, r); return 1; +} + +static int vec4Lerp(lua_State *L) { + vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); + vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); + float_t t = (float_t)luaL_checknumber(L, 3); + vec4 r; glm_vec4_lerp(*a, *b, t, r); + luaVec4Push(L, r); return 1; +} + +static int vec4Negate(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 vec4Create(lua_State *L) { + 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; +} + +void moduleMathVec4(lua_State *L) { + if(!luaL_newmetatable(L, "vec4_mt")) { lua_pop(L, 1); return; } + + 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"); + lua_pop(L, 1); + + lua_register(L, "vec4", vec4Create); +} diff --git a/src/dusk/script/module/math/modulevec4.h b/src/dusk/script/module/math/modulevec4.h new file mode 100644 index 00000000..fcead9a4 --- /dev/null +++ b/src/dusk/script/module/math/modulevec4.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2026 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "script/scriptcontext.h" + +void luaVec4Push(lua_State *L, vec4 v); +void luaVec4Check(lua_State *L, int idx, vec4 out); +void moduleMathVec4(lua_State *L); diff --git a/src/dusk/script/module/scene/modulescene.c b/src/dusk/script/module/scene/modulescene.c index b0024a5c..13575607 100644 --- a/src/dusk/script/module/scene/modulescene.c +++ b/src/dusk/script/module/scene/modulescene.c @@ -13,6 +13,9 @@ void moduleScene(scriptcontext_t *ctx) { assertNotNull(ctx, "Script context cannot be NULL"); lua_register(ctx->luaState, "sceneSet", moduleSceneSet); + + lua_pushlightuserdata(ctx->luaState, &SCENE.eventUpdate); + lua_setglobal(ctx->luaState, "SCENE_EVENT_UPDATE"); } int moduleSceneSet(lua_State *L) { diff --git a/src/dusk/script/scriptmodule.c b/src/dusk/script/scriptmodule.c index d0e7abe2..1b08ea15 100644 --- a/src/dusk/script/scriptmodule.c +++ b/src/dusk/script/scriptmodule.c @@ -17,6 +17,7 @@ #include "script/module/display/modulecolor.h" #include "script/module/display/modulespritebatch.h" #include "script/module/display/moduleglm.h" +#include "script/module/math/modulemath.h" #include "script/module/display/moduleshader.h" #include "script/module/ui/moduleui.h" #include "script/module/display/moduletext.h" @@ -52,6 +53,7 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = { REG("event", moduleEvent) REG("spritebatch", moduleSpriteBatch) REG("glm", moduleGLM) + REG("math", moduleMath) REG("ui", moduleUi) REG("text", moduleText) REG("screen", moduleScreen)