This commit is contained in:
2026-04-28 08:04:01 -05:00
parent 19f2a2c616
commit a41b0e916b
57 changed files with 5023 additions and 3503 deletions
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -4,11 +4,13 @@
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
# Setup # Setup
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 4.0)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
# [cmake] This is allowed only when policy CMP0079 is set to NEW.
cmake_policy(SET CMP0079 NEW)
option(DUSK_BUILD_TESTS "Enable tests" OFF) option(DUSK_BUILD_TESTS "Enable tests" OFF)
+32
View File
@@ -0,0 +1,32 @@
var Cube = {
create: function() {
var e = Entity.create();
e.add(COMPONENT_TYPE_POSITION);
e.position.x = 0;
e.position.y = 0;
e.position.z = 0;
e.add(COMPONENT_TYPE_MESH);
e.add(COMPONENT_TYPE_MATERIAL);
e.material.setColor(colorRed());
return {
_e: e,
update: Cube.update,
dispose: Cube.dispose
};
},
update: function() {
var speed = 3.0;
var dx = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT);
var dz = inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN);
this._e.position.x += dx * speed * TIME.delta;
this._e.position.z += dz * speed * TIME.delta;
},
dispose: function() {
this._e.dispose();
}
};
Cube;
-25
View File
@@ -1,25 +0,0 @@
local Cube = setmetatable({}, { __index = Entity })
Cube.__index = Cube
function Cube.new()
local self = Entity.new()
setmetatable(self, Cube)
self:add(Entity.POSITION)
self.position.x = 0
self.position.y = 0
self.position.z = 0
self:add(Entity.MESH)
self:add(Entity.MATERIAL)
self.material.color = colorRed()
return self
end
function Cube:update()
local speed = 3.0
local dx = inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT)
local dz = inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN)
self.position.x = self.position.x + dx * speed * TIME.delta
self.position.z = self.position.z + dz * speed * TIME.delta
end
return Cube
+75
View File
@@ -0,0 +1,75 @@
// Default input bindings.
if (typeof PSP !== 'undefined') {
inputBind("up", INPUT_ACTION_UP);
inputBind("down", INPUT_ACTION_DOWN);
inputBind("left", INPUT_ACTION_LEFT);
inputBind("right", INPUT_ACTION_RIGHT);
inputBind("accept", INPUT_ACTION_ACCEPT);
inputBind("cancel", INPUT_ACTION_CANCEL);
inputBind("select", INPUT_ACTION_RAGEQUIT);
inputBind("lstick_up", INPUT_ACTION_UP);
inputBind("lstick_down", INPUT_ACTION_DOWN);
inputBind("lstick_left", INPUT_ACTION_LEFT);
inputBind("lstick_right", INPUT_ACTION_RIGHT);
inputBind("triangle", INPUT_ACTION_CONSOLE);
} else if (typeof DOLPHIN !== 'undefined') {
inputBind("up", INPUT_ACTION_UP);
inputBind("down", INPUT_ACTION_DOWN);
inputBind("left", INPUT_ACTION_LEFT);
inputBind("right", INPUT_ACTION_RIGHT);
inputBind("b", INPUT_ACTION_CANCEL);
inputBind("a", INPUT_ACTION_ACCEPT);
inputBind("z", INPUT_ACTION_CONSOLE);
inputBind("lstick_up", INPUT_ACTION_UP);
inputBind("lstick_down", INPUT_ACTION_DOWN);
inputBind("lstick_left", INPUT_ACTION_LEFT);
inputBind("lstick_right", INPUT_ACTION_RIGHT);
} else if (typeof LINUX !== 'undefined') {
if (typeof INPUT_KEYBOARD !== 'undefined') {
inputBind("w", INPUT_ACTION_UP);
inputBind("s", INPUT_ACTION_DOWN);
inputBind("a", INPUT_ACTION_LEFT);
inputBind("d", INPUT_ACTION_RIGHT);
inputBind("left", INPUT_ACTION_LEFT);
inputBind("right", INPUT_ACTION_RIGHT);
inputBind("up", INPUT_ACTION_UP);
inputBind("down", INPUT_ACTION_DOWN);
inputBind("enter", INPUT_ACTION_ACCEPT);
inputBind("e", INPUT_ACTION_ACCEPT);
inputBind("q", INPUT_ACTION_CANCEL);
inputBind("escape", INPUT_ACTION_RAGEQUIT);
inputBind("`", INPUT_ACTION_CONSOLE);
}
if (typeof INPUT_GAMEPAD !== 'undefined') {
inputBind("gamepad_up", INPUT_ACTION_UP);
inputBind("gamepad_down", INPUT_ACTION_DOWN);
inputBind("gamepad_left", INPUT_ACTION_LEFT);
inputBind("gamepad_right", INPUT_ACTION_RIGHT);
inputBind("gamepad_a", INPUT_ACTION_ACCEPT);
inputBind("gamepad_b", INPUT_ACTION_CANCEL);
inputBind("gamepad_back", INPUT_ACTION_RAGEQUIT);
inputBind("gamepad_lstick_up", INPUT_ACTION_UP);
inputBind("gamepad_lstick_down", INPUT_ACTION_DOWN);
inputBind("gamepad_lstick_left", INPUT_ACTION_LEFT);
inputBind("gamepad_lstick_right", INPUT_ACTION_RIGHT);
}
if (typeof INPUT_POINTER !== 'undefined') {
inputBind("mouse_x", INPUT_ACTION_POINTERX);
inputBind("mouse_y", INPUT_ACTION_POINTERY);
}
} else {
consolePrint("Unknown platform, no default input bindings set.");
}
Scene.set('scenes/cube.js');
-75
View File
@@ -1,75 +0,0 @@
-- Default Input bindings.
if PSP then
inputBind("up", INPUT_ACTION_UP)
inputBind("down", INPUT_ACTION_DOWN)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("accept", INPUT_ACTION_ACCEPT)
inputBind("cancel", INPUT_ACTION_CANCEL)
inputBind("select", INPUT_ACTION_RAGEQUIT)
inputBind("lstick_up", INPUT_ACTION_UP)
inputBind("lstick_down", INPUT_ACTION_DOWN)
inputBind("lstick_left", INPUT_ACTION_LEFT)
inputBind("lstick_right", INPUT_ACTION_RIGHT)
inputBind("triangle", INPUT_ACTION_CONSOLE)
elseif DOLPHIN then
inputBind("up", INPUT_ACTION_UP)
inputBind("down", INPUT_ACTION_DOWN)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("b", INPUT_ACTION_CANCEL)
inputBind("a", INPUT_ACTION_ACCEPT)
inputBind("z", INPUT_ACTION_CONSOLE)
inputBind("lstick_up", INPUT_ACTION_UP)
inputBind("lstick_down", INPUT_ACTION_DOWN)
inputBind("lstick_left", INPUT_ACTION_LEFT)
inputBind("lstick_right", INPUT_ACTION_RIGHT)
elseif LINUX then
if INPUT_KEYBOARD then
inputBind("w", INPUT_ACTION_UP)
inputBind("s", INPUT_ACTION_DOWN)
inputBind("a", INPUT_ACTION_LEFT)
inputBind("d", INPUT_ACTION_RIGHT)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("up", INPUT_ACTION_UP)
inputBind("down", INPUT_ACTION_DOWN)
inputBind("enter", INPUT_ACTION_ACCEPT)
inputBind("e", INPUT_ACTION_ACCEPT)
inputBind("q", INPUT_ACTION_CANCEL)
inputBind("escape", INPUT_ACTION_RAGEQUIT)
inputBind("`", INPUT_ACTION_CONSOLE)
end
if INPUT_GAMEPAD then
inputBind("gamepad_up", INPUT_ACTION_UP)
inputBind("gamepad_down", INPUT_ACTION_DOWN)
inputBind("gamepad_left", INPUT_ACTION_LEFT)
inputBind("gamepad_right", INPUT_ACTION_RIGHT)
inputBind("gamepad_a", INPUT_ACTION_ACCEPT)
inputBind("gamepad_b", INPUT_ACTION_CANCEL)
inputBind("gamepad_back", INPUT_ACTION_RAGEQUIT)
inputBind("gamepad_lstick_up", INPUT_ACTION_UP)
inputBind("gamepad_lstick_down", INPUT_ACTION_DOWN)
inputBind("gamepad_lstick_left", INPUT_ACTION_LEFT)
inputBind("gamepad_lstick_right", INPUT_ACTION_RIGHT)
end
if INPUT_POINTER then
inputBind("mouse_x", INPUT_ACTION_POINTERX)
inputBind("mouse_y", INPUT_ACTION_POINTERY)
end
else
print("Unknown platform, no default input bindings set.")
end
Scene.set('scenes/cube.lua')
+29
View File
@@ -0,0 +1,29 @@
var Cube = include('entities/cube.js');
var cam;
var cube;
var SceneCube = {
init: function() {
cam = Entity.create();
cam.add(COMPONENT_TYPE_POSITION);
cam.position.x = 3;
cam.position.y = 3;
cam.position.z = 3;
cam.position.lookAt(0, 0, 0);
cam.add(COMPONENT_TYPE_CAMERA);
cube = Cube.create();
},
update: function() {
cube.update();
},
dispose: function() {
cam.dispose();
cube.dispose();
}
};
SceneCube;
-30
View File
@@ -1,30 +0,0 @@
local Cube = include('entities/cube.lua')
local SceneCube = {}
SceneCube.__index = SceneCube
local cam
local cube
function SceneCube:init()
cam = Entity.new()
cam:add(Entity.POSITION)
cam.position.x = 3
cam.position.y = 3
cam.position.z = 3
cam.position:lookAt(0, 0, 0)
cam:add(Entity.CAMERA)
cube = Cube.new()
end
function SceneCube:update()
cube:update()
end
function SceneCube:dispose()
cam:dispose()
cube:dispose()
end
return SceneCube
+14
View File
@@ -0,0 +1,14 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
include(FetchContent)
FetchContent_Declare(
jerryscript
GIT_REPOSITORY https://github.com/jerryscript-project/jerryscript
GIT_TAG v2.4.0
)
FetchContent_MakeAvailable(jerryscript)
set(jerryscript_FOUND ON)
-22
View File
@@ -22,31 +22,9 @@ set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE) set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
find_package(cglm REQUIRED) find_package(cglm REQUIRED)
# Compile lua
include(FetchContent)
FetchContent_Declare(
liblua
URL https://www.lua.org/ftp/lua-5.5.0.tar.gz
)
FetchContent_MakeAvailable(liblua)
set(LUA_SRC_DIR "${liblua_SOURCE_DIR}/src")
set(LUA_C_FILES
lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c ldblib.c ldebug.c
ldo.c ldump.c lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c
loadlib.c lobject.c lopcodes.c loslib.c lparser.c lstate.c lstring.c
lstrlib.c ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c lzio.c
)
list(TRANSFORM LUA_C_FILES PREPEND "${LUA_SRC_DIR}/")
add_library(liblua STATIC ${LUA_C_FILES})
target_include_directories(liblua PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(liblua PUBLIC LUA_USE_C89)
add_library(lua::lua ALIAS liblua)
set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE)
# Link libraries # Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
cglm cglm
liblua
m m
fat fat
PkgConfig::zip PkgConfig::zip
-22
View File
@@ -20,31 +20,9 @@ set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE) set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
find_package(cglm REQUIRED) find_package(cglm REQUIRED)
# Compile lua
include(FetchContent)
FetchContent_Declare(
liblua
URL https://www.lua.org/ftp/lua-5.5.0.tar.gz
)
FetchContent_MakeAvailable(liblua)
set(LUA_SRC_DIR "${liblua_SOURCE_DIR}/src")
set(LUA_C_FILES
lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c ldblib.c ldebug.c
ldo.c ldump.c lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c
loadlib.c lobject.c lopcodes.c loslib.c lparser.c lstate.c lstring.c
lstrlib.c ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c lzio.c
)
list(TRANSFORM LUA_C_FILES PREPEND "${LUA_SRC_DIR}/")
add_library(liblua STATIC ${LUA_C_FILES})
target_include_directories(liblua PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(liblua PRIVATE LUA_USE_C89)
add_library(lua::lua ALIAS liblua)
set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE)
# Link libraries # Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${SDL2_LIBRARIES} ${SDL2_LIBRARIES}
liblua
cglm cglm
SDL2 SDL2
SDL2main SDL2main
+3 -12
View File
@@ -32,18 +32,9 @@ if(NOT yyjson_FOUND)
endif() endif()
endif() endif()
if(NOT Lua_FOUND) if(NOT jerryscript_FOUND)
find_package(Lua REQUIRED) find_package(jerryscript REQUIRED)
if(Lua_FOUND AND NOT TARGET Lua::Lua) target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC jerryscript)
add_library(Lua::Lua INTERFACE IMPORTED)
set_target_properties(
Lua::Lua
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
)
endif()
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC Lua::Lua)
endif() endif()
# Includes # Includes
@@ -1,82 +1,95 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#include "assetscriptloader.h" #include "assetscriptloader.h"
#include "assert/assert.h" #include "assert/assert.h"
#include <stdlib.h>
#include <zip.h>
errorret_t assetScriptLoader(assetfile_t *file) { errorret_t assetScriptLoader(assetfile_t *file) {
assertNotNull(file, "Asset file cannot be NULL"); assertNotNull(file, "Asset file cannot be NULL");
assertNull(file->zipFile, "Asset file zip handle must be NULL"); assertNull(file->zipFile, "Asset file zip handle must be NULL before open");
assertNotNull(file->output, "Asset file output cannot be NULL"); assertNotNull(file->output, "Asset file output cannot be NULL");
assetscript_t *script = (assetscript_t *)file->output; assetscript_t *script = (assetscript_t *)file->output;
// Open the asset for buffering
errorChain(assetFileOpen(file)); errorChain(assetFileOpen(file));
// Request loading // Accumulate full source into a dynamically grown buffer.
if(lua_load( size_t srcLen = 0;
script->ctx->luaState, size_t capacity = ASSET_SCRIPT_CHUNK_SIZE;
assetScriptReader, char_t *src = (char_t *)malloc(capacity + 1);
file, if(!src) {
file->filename, assetFileClose(file);
NULL errorThrow("Out of memory reading script: %s", file->filename);
) != LUA_OK) {
const char_t *strErr = lua_tostring(script->ctx->luaState, -1);
lua_pop(script->ctx->luaState, 1);
errorThrow("Failed to load Lua script: %s", strErr);
} }
// Now loaded, exec while(1) {
if(lua_pcall(script->ctx->luaState, 0, LUA_MULTRET, 0) != LUA_OK) { if(srcLen + ASSET_SCRIPT_CHUNK_SIZE > capacity) {
const char_t *strErr = lua_tostring(script->ctx->luaState, -1); capacity = srcLen + ASSET_SCRIPT_CHUNK_SIZE;
lua_pop(script->ctx->luaState, 1); char_t *tmp = (char_t *)realloc(src, capacity + 1);
errorThrow("Failed to execute Lua script: %s", strErr); if(!tmp) {
free(src);
assetFileClose(file);
errorThrow("Out of memory reading script: %s", file->filename);
}
src = tmp;
}
zip_int64_t n = zip_fread(
file->zipFile, src + srcLen, ASSET_SCRIPT_CHUNK_SIZE
);
if(n <= 0) break;
srcLen += (size_t)n;
}
src[srcLen] = '\0';
errorret_t closeRet = assetFileClose(file);
jerry_value_t result = jerry_eval(
(const jerry_char_t *)src,
srcLen,
JERRY_PARSE_NO_OPTS
);
free(src);
if(jerry_value_is_exception(result)) {
jerry_value_t errVal = jerry_exception_value(result, false);
jerry_value_t errStr = jerry_value_to_string(errVal);
char_t buf[256];
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(errStr);
jerry_value_free(errVal);
jerry_value_free(result);
errorThrow("Script error in '%s': %s", file->filename, buf);
} }
// Close the file if(script->resultOut != NULL) {
return assetFileClose(file); *(script->resultOut) = result;
} else {
jerry_value_free(result);
}
return closeRet;
} }
errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx) { errorret_t assetScriptLoad(
const char_t *path,
scriptcontext_t *ctx,
jerry_value_t *resultOut
) {
assertNotNull(path, "Script path cannot be NULL"); assertNotNull(path, "Script path cannot be NULL");
assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(ctx, "Script context cannot be NULL");
assetscript_t script;
script.ctx = ctx;
return assetLoad( assetscript_t scriptData;
path, scriptData.ctx = ctx;
assetScriptLoader, scriptData.resultOut = resultOut;
NULL,
&script return assetLoad(path, assetScriptLoader, NULL, &scriptData);
);
} }
const char_t * assetScriptReader(lua_State* L, void* data, size_t* size) {
assetfile_t *file = (assetfile_t*)data;
assertNotNull(file, "Script asset file cannot be NULL");
assertNotNull(file->zipFile, "Script asset zip handle cannot be NULL");
assertNotNull(file->output, "Script asset output cannot be NULL");
assetscript_t *script = (assetscript_t *)file->output;
assertNotNull(script, "Script asset output cannot be NULL");
zip_int64_t read = zip_fread(
file->zipFile,
script->buffer,
sizeof(script->buffer)
);
if(read < 0) {
*size = 0;
return NULL;
}
*size = (size_t)read;
return script->buffer;
}
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -9,7 +9,7 @@
#include "asset/asset.h" #include "asset/asset.h"
#include "script/scriptcontext.h" #include "script/scriptcontext.h"
#define ASSET_SCRIPT_BUFFER_SIZE 1024 #define ASSET_SCRIPT_CHUNK_SIZE 1024
typedef struct { typedef struct {
void *nothing; void *nothing;
@@ -17,12 +17,13 @@ typedef struct {
typedef struct { typedef struct {
scriptcontext_t *ctx; scriptcontext_t *ctx;
char_t buffer[ASSET_SCRIPT_BUFFER_SIZE]; jerry_value_t *resultOut;
} assetscript_t; } assetscript_t;
/** /**
* Handler for script assets. * Handler for script assets. Reads the full source, evaluates it with
* * JerryScript, and optionally stores the result.
*
* @param file Asset file to load the script from. * @param file Asset file to load the script from.
* @return Any error that occurs during loading. * @return Any error that occurs during loading.
*/ */
@@ -30,19 +31,16 @@ errorret_t assetScriptLoader(assetfile_t *file);
/** /**
* Loads a script from the specified path. * Loads a script from the specified path.
* *
* @param path Path to the script asset. * @param path Path to the script asset.
* @param ctx Script context to load the script into. * @param ctx Script context to load the script into.
* @param resultOut Optional out-parameter for the script return value.
* Caller must call jerry_value_free() if non-NULL.
* Pass NULL to discard the return value.
* @return Any error that occurs during loading. * @return Any error that occurs during loading.
*/ */
errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx); errorret_t assetScriptLoad(
const char_t *path,
/** scriptcontext_t *ctx,
* Reader function for Lua to read script data from the asset. jerry_value_t *resultOut
* );
* @param L Lua state.
* @param data Pointer to the scriptcontext_t structure.
* @param size Pointer to store the size of the read data.
* @return Pointer to the read data buffer.
*/
const char_t * assetScriptReader(lua_State* L, void* data, size_t* size);
+1 -1
View File
@@ -50,7 +50,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
/* Run the init script. */ /* Run the init script. */
consolePrint("Engine initialized"); consolePrint("Engine initialized");
errorChain(scriptContextExecFile(&SCRIPT_MANAGER.mainContext, "init.lua")); errorChain(scriptContextExecFile(&SCRIPT_MANAGER.mainContext, "init.js", NULL));
errorOk(); errorOk();
} }
+54 -79
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -10,16 +10,6 @@
#include "util/memory.h" #include "util/memory.h"
#include "console/console.h" #include "console/console.h"
static int eventLuaTraceback(lua_State *L) {
const char *msg = lua_tostring(L, 1);
if(msg) {
luaL_traceback(L, L, msg, 1);
} else {
lua_pushliteral(L, "(no error message)");
}
return 1;
}
void eventInit( void eventInit(
event_t *event, event_t *event,
eventlistener_t *array, eventlistener_t *array,
@@ -57,19 +47,16 @@ eventsub_t eventSubscribeUser(
"Script event listener context cannot be NULL" "Script event listener context cannot be NULL"
); );
assertTrue( assertTrue(
user.script.luaFunctionRef != LUA_NOREF, user.script.funcValue != 0,
"Script event listener function reference is invalid" "Script event listener function reference is invalid"
); );
} else { } else {
assertUnreachable("Unknown event listener type"); assertUnreachable("Unknown event listener type");
} }
// Gen a new ID
eventsub_t id = event->nextId++; eventsub_t id = event->nextId++;
// Did we wrap?
assertTrue(event->nextId != 0, "Event subscription ID overflow"); assertTrue(event->nextId != 0, "Event subscription ID overflow");
// Append listener
eventlistener_t *listener = &event->listenerArray[event->listenerCount++]; eventlistener_t *listener = &event->listenerArray[event->listenerCount++];
memoryZero(listener, sizeof(eventlistener_t)); memoryZero(listener, sizeof(eventlistener_t));
listener->user = user; listener->user = user;
@@ -94,49 +81,29 @@ eventsub_t eventSubscribe(
eventsub_t eventSubscribeScriptContext( eventsub_t eventSubscribeScriptContext(
event_t *event, event_t *event,
scriptcontext_t *context, scriptcontext_t *context,
const int functionIndex jerry_value_t funcValue
) { ) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
assertTrue( assertTrue(funcValue != 0, "Script function value is invalid");
lua_isfunction(context->luaState, functionIndex),
"Expected function at given index"
);
// Create a reference to the function
lua_pushvalue(context->luaState, functionIndex);
int funcRef = luaL_ref(context->luaState, LUA_REGISTRYINDEX);
eventscript_t scriptUser = {
.context = context,
.luaFunctionRef = funcRef
};
// Note to the context that it is now a part of this event
bool_t alreadySubbed = false; bool_t alreadySubbed = false;
uint8_t i; uint8_t i = 0;
i = 0;
do { do {
if(context->subscribedEvents[i] != event) { if(context->subscribedEvents[i] == event) {
i++; alreadySubbed = true;
continue; break;
} }
i++;
if(context->subscribedEvents[i] == NULL) break;
alreadySubbed = true;
break;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS); } while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
if(!alreadySubbed) { if(!alreadySubbed) {
i = 0; i = 0;
do { do {
if(context->subscribedEvents[i] != NULL) { if(context->subscribedEvents[i] == NULL) {
i++; context->subscribedEvents[i] = event;
continue; break;
} }
i++;
context->subscribedEvents[i] = event;
break;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS); } while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
assertTrue( assertTrue(
@@ -148,7 +115,12 @@ eventsub_t eventSubscribeScriptContext(
return eventSubscribeUser( return eventSubscribeUser(
event, event,
EVENT_TYPE_SCRIPT, EVENT_TYPE_SCRIPT,
(eventuserdata_t){ .script = scriptUser } (eventuserdata_t){
.script = {
.context = context,
.funcValue = funcValue
}
}
); );
} }
@@ -165,19 +137,13 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) {
continue; continue;
} }
// Release Lua registry reference before the slot is overwritten
if(event->listenerArray[index].type == EVENT_TYPE_SCRIPT) { if(event->listenerArray[index].type == EVENT_TYPE_SCRIPT) {
scriptcontext_t *ctx = event->listenerArray[index].user.script.context; jerry_value_t funcVal = event->listenerArray[index].user.script.funcValue;
if(ctx != NULL && ctx->luaState != NULL) { if(funcVal != 0) {
luaL_unref( jerry_value_free(funcVal);
ctx->luaState,
LUA_REGISTRYINDEX,
event->listenerArray[index].user.script.luaFunctionRef
);
} }
} }
// Swap with last and shrink
event->listenerArray[index] = event->listenerArray[--event->listenerCount]; event->listenerArray[index] = event->listenerArray[--event->listenerCount];
return; return;
} while(index < event->listenerCount); } while(index < event->listenerCount);
@@ -188,7 +154,7 @@ void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(ctx, "Script context cannot be NULL");
if(event->listenerCount == 0) return; if(event->listenerCount == 0) return;
uint16_t i = 0; uint16_t i = 0;
do { do {
eventlistener_t *listener = &event->listenerArray[i]; eventlistener_t *listener = &event->listenerArray[i];
@@ -199,9 +165,6 @@ void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
i++; i++;
continue; continue;
} }
// This listener belongs to the context and will need to go away. This will
// in turn decrement the listener count so we don't increment i here.
eventUnsubscribe(event, listener->id); eventUnsubscribe(event, listener->id);
} while(i < event->listenerCount); } while(i < event->listenerCount);
} }
@@ -214,43 +177,55 @@ void eventInvoke(
assertNotNull(event, "Event cannot be NULL"); assertNotNull(event, "Event cannot be NULL");
if(event->listenerCount == 0) return; if(event->listenerCount == 0) return;
event->isInvoking = true; event->isInvoking = true;
uint16_t i = 0; eventdata_t data = {
eventdata_t data ={
.event = event, .event = event,
.eventParams = eventParams, .eventParams = eventParams,
}; };
uint16_t i = 0;
do { do {
eventlistener_t *listener = &event->listenerArray[i]; eventlistener_t *listener = &event->listenerArray[i];
if(listener->type == EVENT_TYPE_C) { if(listener->type == EVENT_TYPE_C) {
listener->user.c.callback(&data, listener->user.c); listener->user.c.callback(&data, listener->user.c);
} else if(listener->type == EVENT_TYPE_SCRIPT) { } else if(listener->type == EVENT_TYPE_SCRIPT) {
lua_State *L = listener->user.script.context->luaState; jerry_value_t funcVal = listener->user.script.funcValue;
assertNotNull(L, "Lua state in event listener cannot be NULL"); assertNotNull((void *)(uintptr_t)funcVal, "Script function value is NULL");
lua_pushcfunction(L, eventLuaTraceback); jerry_value_t callArgs[1];
int handlerIdx = lua_gettop(L); jerry_length_t argCount = 0;
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
int numArgs = 0;
if(eventParams != NULL) { if(eventParams != NULL) {
lua_pushlightuserdata(L, (void *)eventParams); callArgs[0] = jerry_object();
numArgs = 1; jerry_object_set_native_ptr(
callArgs[0], &JS_PTR_NATIVE_INFO, (void *)eventParams
);
argCount = 1;
} }
printf("Invoking Lua event listener with %d argument(s)\n", numArgs); jerry_value_t result = jerry_call(
funcVal, jerry_undefined(), callArgs, argCount
);
if(lua_pcall(L, numArgs, 0, handlerIdx) != LUA_OK) { if(argCount > 0) jerry_value_free(callArgs[0]);
const char_t *strErr = lua_tostring(L, -1);
consolePrint("Error invoking Lua event listener:\n%s\n", strErr); if(jerry_value_is_exception(result)) {
lua_pop(L, 1); jerry_value_t errStr = jerry_value_to_string(
jerry_exception_value(result, false)
);
char_t buf[256];
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(errStr);
consolePrint("Error invoking script event listener:\n%s\n", buf);
} }
lua_pop(L, 1); jerry_value_free(result);
} else { } else {
assertUnreachable("Unknown event listener type"); assertUnreachable("Unknown event listener type");
} }
@@ -258,4 +233,4 @@ void eventInvoke(
} while(i < event->listenerCount); } while(i < event->listenerCount);
event->isInvoking = false; event->isInvoking = false;
} }
+1 -1
View File
@@ -84,7 +84,7 @@ eventsub_t eventSubscribe(
eventsub_t eventSubscribeScriptContext( eventsub_t eventSubscribeScriptContext(
event_t *event, event_t *event,
scriptcontext_t *context, scriptcontext_t *context,
const int functionIndex jerry_value_t funcValue
); );
/** /**
+3 -3
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -16,7 +16,7 @@ typedef enum {
typedef struct { typedef struct {
scriptcontext_t *context; scriptcontext_t *context;
int luaFunctionRef; jerry_value_t funcValue;
} eventscript_t; } eventscript_t;
typedef struct eventc_s { typedef struct eventc_s {
@@ -27,4 +27,4 @@ typedef struct eventc_s {
typedef union eventuserdata_u { typedef union eventuserdata_u {
eventscript_t script; eventscript_t script;
eventc_t c; eventc_t c;
} eventuserdata_t; } eventuserdata_t;
+15 -31
View File
@@ -24,7 +24,7 @@ scene_t SCENE;
errorret_t sceneInit(void) { errorret_t sceneInit(void) {
memoryZero(&SCENE, sizeof(scene_t)); memoryZero(&SCENE, sizeof(scene_t));
SCENE.scriptRef = LUA_NOREF; SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
errorOk(); errorOk();
} }
@@ -40,25 +40,22 @@ errorret_t sceneUpdate(void) {
} }
if(SCENE.sceneActive) { if(SCENE.sceneActive) {
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "update")); errorChain(moduleSceneCall("update"));
} }
errorOk(); errorOk();
} }
errorret_t sceneRender(void) { errorret_t sceneRender(void) {
// Get Cameras
entityid_t camEnts[ENTITY_COUNT_MAX]; entityid_t camEnts[ENTITY_COUNT_MAX];
componentid_t camComps[ENTITY_COUNT_MAX]; componentid_t camComps[ENTITY_COUNT_MAX];
entityid_t camCount = componentGetEntitiesWithComponent( entityid_t camCount = componentGetEntitiesWithComponent(
COMPONENT_TYPE_CAMERA, camEnts, camComps COMPONENT_TYPE_CAMERA, camEnts, camComps
); );
// Prep Matricies
mat4 view, proj, model; mat4 view, proj, model;
if(camCount > 0) { if(camCount > 0) {
// Get meshes
entityid_t meshEnts[ENTITY_COUNT_MAX]; entityid_t meshEnts[ENTITY_COUNT_MAX];
componentid_t meshComps[ENTITY_COUNT_MAX]; componentid_t meshComps[ENTITY_COUNT_MAX];
entityid_t meshCount = componentGetEntitiesWithComponent( entityid_t meshCount = componentGetEntitiesWithComponent(
@@ -68,7 +65,6 @@ errorret_t sceneRender(void) {
if(meshCount > 0) { if(meshCount > 0) {
errorChain(shaderBind(&SHADER_UNLIT)); errorChain(shaderBind(&SHADER_UNLIT));
// For each camera.
for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) { for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
entityid_t camEnt = camEnts[camIndex]; entityid_t camEnt = camEnts[camIndex];
componentid_t camComp = camComps[camIndex]; componentid_t camComp = camComps[camIndex];
@@ -81,15 +77,11 @@ errorret_t sceneRender(void) {
entityCameraGetProjection(camEnt, camComp, proj); entityCameraGetProjection(camEnt, camComp, proj);
entityPositionGetTransform(camEnt, camPos, view); entityPositionGetTransform(camEnt, camPos, view);
// For each mesh.
for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) { for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
entityid_t meshEnt = meshEnts[meshIndex]; entityid_t meshEnt = meshEnts[meshIndex];
componentid_t meshComp = meshComps[meshIndex]; componentid_t meshComp = meshComps[meshIndex];
mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp); mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
if(mesh == NULL) { if(mesh == NULL) continue;
continue;
}
componentid_t meshPos = entityGetComponent( componentid_t meshPos = entityGetComponent(
meshEnt, COMPONENT_TYPE_POSITION meshEnt, COMPONENT_TYPE_POSITION
@@ -129,7 +121,6 @@ errorret_t sceneRender(void) {
} }
} }
// Here is where UI will go
glm_ortho( glm_ortho(
0.0f, SCREEN.width, 0.0f, SCREEN.width,
SCREEN.height, 0.0f, SCREEN.height, 0.0f,
@@ -163,15 +154,11 @@ errorret_t sceneSetImmediate(const char_t *scene) {
} }
if(SCENE.sceneActive) { if(SCENE.sceneActive) {
errorChain(moduleSceneCall( errorChain(moduleSceneCall("dispose"));
SCRIPT_MANAGER.mainContext.luaState, "dispose"
));
SCENE.sceneActive = false; SCENE.sceneActive = false;
} }
// Wipe Scene back to a clean table before loading the next file so custom moduleSceneReset();
// methods from the previous scene (e.g. Scene.doThing) cannot bleed through.
moduleSceneReset(SCRIPT_MANAGER.mainContext.luaState);
stringCopy( stringCopy(
SCENE.sceneCurrent, SCENE.sceneCurrent,
@@ -180,22 +167,19 @@ errorret_t sceneSetImmediate(const char_t *scene) {
); );
if(scene != NULL) { if(scene != NULL) {
lua_State *L = SCRIPT_MANAGER.mainContext.luaState; jerry_value_t sceneObj = SCENE_SCRIPT_REF_NONE;
errorChain(scriptContextExecFile(
&SCRIPT_MANAGER.mainContext, scene, &sceneObj
));
int32_t stackBase = lua_gettop(L); if(!jerry_value_is_object(sceneObj)) {
if(sceneObj != SCENE_SCRIPT_REF_NONE) jerry_value_free(sceneObj);
errorChain(scriptContextExecFile(&SCRIPT_MANAGER.mainContext, scene)); errorThrow("Scene '%s' must return an object", scene);
int32_t nReturns = lua_gettop(L) - stackBase;
if(nReturns < 1 || !lua_istable(L, stackBase + 1)) {
lua_settop(L, stackBase);
errorThrow("Scene '%s' must return a table", scene);
} }
lua_settop(L, stackBase + 1);
SCENE.scriptRef = luaL_ref(L, LUA_REGISTRYINDEX); SCENE.scriptRef = sceneObj;
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "init")); errorChain(moduleSceneCall("init"));
SCENE.sceneActive = true; SCENE.sceneActive = true;
} }
@@ -211,6 +195,6 @@ void sceneSet(const char_t *scene) {
} }
errorret_t sceneDispose(void) { errorret_t sceneDispose(void) {
errorChain(moduleSceneCall(SCRIPT_MANAGER.mainContext.luaState, "dispose")); errorChain(moduleSceneCall("dispose"));
errorOk(); errorOk();
} }
+15 -23
View File
@@ -14,63 +14,55 @@
typedef struct { typedef struct {
bool_t sceneActive; bool_t sceneActive;
int scriptRef; jerry_value_t scriptRef;
char_t sceneCurrent[ASSET_FILE_PATH_MAX]; char_t sceneCurrent[ASSET_FILE_PATH_MAX];
char_t sceneNext[ASSET_FILE_PATH_MAX]; char_t sceneNext[ASSET_FILE_PATH_MAX];
} scene_t; } scene_t;
extern scene_t SCENE; extern scene_t SCENE;
/** Sentinel value meaning no scene script is loaded. */
#define SCENE_SCRIPT_REF_NONE ((jerry_value_t)0)
/** /**
* Initializes the scene manager * Initializes the scene manager.
* *
* @return Any error state that happened. * @return Any error state that happened.
*/ */
errorret_t sceneInit(void); errorret_t sceneInit(void);
/** /**
* Ticks the scene manager, may or may not call the scene's update method * Ticks the scene manager; may call the scene's update method.
* depending on time state. *
*
* @return Any error state that happened. * @return Any error state that happened.
*/ */
errorret_t sceneUpdate(void); errorret_t sceneUpdate(void);
/** /**
* Renders the scene, happens regardless of time. * Renders the scene.
* *
* @return Any error state that happened. * @return Any error state that happened.
*/ */
errorret_t sceneRender(void); errorret_t sceneRender(void);
/** /**
* Internal method to immediately switch scenes, this will also delete some of * Immediately switches scenes, disposing the current one first.
* the lua state, which can cause problems if called during a lua callback, so *
* this is managed by the scene manager.
*
* @param scene Scene to switch to (asset file path). * @param scene Scene to switch to (asset file path).
* @return Any error state that happened. * @return Any error state that happened.
*/ */
errorret_t sceneSetImmediate(const char_t *scene); errorret_t sceneSetImmediate(const char_t *scene);
/** /**
* Requests the scene manager to change scenes when it is next able to. This * Requests a scene change on the next safe opportunity.
* will not guarantee that the scene is valid or without errors. *
*
* @param scene Which scene to set. * @param scene Which scene to set.
*/ */
void sceneSet(const char_t *scene); void sceneSet(const char_t *scene);
/** /**
* Disposes of the current scene. * Disposes of the current scene.
*
* @return Any error state that happened. * @return Any error state that happened.
*/ */
errorret_t sceneDispose(void); errorret_t sceneDispose(void);
/**
* Lua callback when Scene.set is invoked.
*
* @param L Lua state, expects the first argument to be the scene name (string).
* @return 0, does not return any values to Lua.
*/
int sceneSetLua(lua_State *L);
+147 -89
View File
@@ -10,112 +10,170 @@
#include "display/color.h" #include "display/color.h"
#include "time/time.h" #include "time/time.h"
static int moduleColorIndex(lua_State *L) { // ---------------------------------------------------------------------------
const color_t *color = (const color_t *)luaL_checkudata(L, 1, "color_mt"); // Native info / prototype
assertNotNull(color, "Color struct cannot be NULL."); // ---------------------------------------------------------------------------
const char_t *key = luaL_checkstring(L, 2);
if(stringCompare(key, "r") == 0) { lua_pushnumber(L, color->r); return 1; } static void freeColorNative(void *ptr, jerry_object_native_info_t *info) {
if(stringCompare(key, "g") == 0) { lua_pushnumber(L, color->g); return 1; } (void)info;
if(stringCompare(key, "b") == 0) { lua_pushnumber(L, color->b); return 1; } free(ptr);
if(stringCompare(key, "a") == 0) { lua_pushnumber(L, color->a); return 1; }
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
return 1;
} }
static const jerry_object_native_info_t COLOR_NATIVE_INFO = {
.free_cb = freeColorNative,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_colorProto = 0;
static int moduleColorNewIndex(lua_State *L) { // ---------------------------------------------------------------------------
color_t *color = (color_t *)luaL_checkudata(L, 1, "color_mt"); // Property getters / setters
assertNotNull(color, "Color struct cannot be NULL."); // ---------------------------------------------------------------------------
const char_t *key = luaL_checkstring(L, 2);
if(stringCompare(key, "r") == 0) { JS_FUNC(moduleColorGetR) {
color->r = (colorchannel8_t)luaL_checknumber(L, 3); return 0; color_t *color = (color_t *)jerry_object_get_native_ptr(
} else if(stringCompare(key, "g") == 0) { call_info_p->this_value, &COLOR_NATIVE_INFO
color->g = (colorchannel8_t)luaL_checknumber(L, 3); return 0;
} else if(stringCompare(key, "b") == 0) {
color->b = (colorchannel8_t)luaL_checknumber(L, 3); return 0;
} else if(stringCompare(key, "a") == 0) {
color->a = (colorchannel8_t)luaL_checknumber(L, 3); return 0;
}
luaL_error(L, "color: unknown property '%s'", key);
return 0;
}
static int moduleColorToString(lua_State *L) {
const color_t *color = (const color_t *)luaL_checkudata(L, 1, "color_mt");
assertNotNull(color, "Color struct cannot be NULL.");
lua_pushfstring(L, "color(r=%d, g=%d, b=%d, a=%d)",
color->r, color->g, color->b, color->a
); );
return 1; return color ? jerry_number(color->r) : jerry_undefined();
} }
JS_FUNC(moduleColorSetR) {
static int moduleColorCreate(lua_State *L) { color_t *color = (color_t *)jerry_object_get_native_ptr(
assertNotNull(L, "Lua state cannot be NULL."); call_info_p->this_value, &COLOR_NATIVE_INFO
);
if( if(color && args_count > 0) {
!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || color->r = (colorchannel8_t)jerry_value_as_number(args_p[0]);
!lua_isnumber(L, 3) || !lua_isnumber(L, 4)
) {
return luaL_error(L, "color(r, g, b, a) requires four number arguments.");
} }
return jerry_undefined();
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
color->r = (colorchannel8_t)lua_tonumber(L, 1);
color->g = (colorchannel8_t)lua_tonumber(L, 2);
color->b = (colorchannel8_t)lua_tonumber(L, 3);
color->a = (colorchannel8_t)lua_tonumber(L, 4);
return 1;
} }
static int moduleColorRainbow(lua_State *L) { JS_FUNC(moduleColorGetG) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
return color ? jerry_number(color->g) : jerry_undefined();
}
JS_FUNC(moduleColorSetG) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
if(color && args_count > 0) {
color->g = (colorchannel8_t)jerry_value_as_number(args_p[0]);
}
return jerry_undefined();
}
JS_FUNC(moduleColorGetB) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
return color ? jerry_number(color->b) : jerry_undefined();
}
JS_FUNC(moduleColorSetB) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
if(color && args_count > 0) {
color->b = (colorchannel8_t)jerry_value_as_number(args_p[0]);
}
return jerry_undefined();
}
JS_FUNC(moduleColorGetA) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
return color ? jerry_number(color->a) : jerry_undefined();
}
JS_FUNC(moduleColorSetA) {
color_t *color = (color_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &COLOR_NATIVE_INFO
);
if(color && args_count > 0) {
color->a = (colorchannel8_t)jerry_value_as_number(args_p[0]);
}
return jerry_undefined();
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
* Creates a new JS color object wrapping a heap-allocated color_t.
*
* @param color Value to copy into the new object.
* @return Owned jerry_value_t with COLOR_NATIVE_INFO and prototype set.
*/
static inline jerry_value_t moduleColorMakeObject(color_t color) {
color_t *ptr = (color_t *)malloc(sizeof(color_t));
*ptr = color;
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &COLOR_NATIVE_INFO, ptr);
if(s_colorProto != 0) jerry_object_set_proto(obj, s_colorProto);
return obj;
}
/**
* Returns the native color_t pointer stored in a JS value, or NULL.
*
* @param val JS value to inspect.
* @return Pointer to the color_t, or NULL if not a color object.
*/
static inline color_t *moduleColorGetNative(jerry_value_t val) {
return (color_t *)jerry_object_get_native_ptr(val, &COLOR_NATIVE_INFO);
}
// ---------------------------------------------------------------------------
// Constructors
// ---------------------------------------------------------------------------
JS_FUNC(moduleColorCreate) {
JS_REQUIRE_ARGS(4);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
JS_REQUIRE_NUMBER(3);
color_t c;
c.r = (colorchannel8_t)jerry_value_as_number(args_p[0]);
c.g = (colorchannel8_t)jerry_value_as_number(args_p[1]);
c.b = (colorchannel8_t)jerry_value_as_number(args_p[2]);
c.a = (colorchannel8_t)jerry_value_as_number(args_p[3]);
return moduleColorMakeObject(c);
}
JS_FUNC(moduleColorRainbow) {
float_t t = TIME.time * 4.0f; float_t t = TIME.time * 4.0f;
if(lua_gettop(L) >= 1) {
if(!lua_isnumber(L, 1)) { if(args_count >= 1 && jerry_value_is_number(args_p[0])) {
return luaL_error(L, "Rainbow time offset must be a number."); t += (float_t)jerry_value_as_number(args_p[0]);
}
t += (float_t)lua_tonumber(L, 1);
} }
if(lua_gettop(L) >= 2) { if(args_count >= 2 && jerry_value_is_number(args_p[1])) {
if(!lua_isnumber(L, 2)) { t *= (float_t)jerry_value_as_number(args_p[1]);
return luaL_error(L, "Rainbow speed multiplier must be a number.");
}
t *= (float_t)lua_tonumber(L, 2);
} }
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t)); color_t c;
color->r = (colorchannel8_t)((sinf(t) + 1.0f) * 0.5f * 255.0f); c.r = (colorchannel8_t)((sinf(t) + 1.0f) * 0.5f * 255.0f);
color->g = (colorchannel8_t)((sinf(t + 2.0f) + 1.0f) * 0.5f * 255.0f); c.g = (colorchannel8_t)((sinf(t + 2.0f) + 1.0f) * 0.5f * 255.0f);
color->b = (colorchannel8_t)((sinf(t + 4.0f) + 1.0f) * 0.5f * 255.0f); c.b = (colorchannel8_t)((sinf(t + 4.0f) + 1.0f) * 0.5f * 255.0f);
color->a = 255; c.a = 255;
return moduleColorMakeObject(c);
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
return 1;
} }
static void moduleColor(lua_State *L) { // ---------------------------------------------------------------------------
assertNotNull(L, "Lua state cannot be NULL."); // Module init
// ---------------------------------------------------------------------------
if(luaL_newmetatable(L, "color_mt")) { static void moduleColor(void) {
lua_pushcfunction(L, moduleColorIndex); s_colorProto = jerry_object();
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleColorNewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleColorToString);
lua_setfield(L, -2, "__tostring");
}
lua_pop(L, 1);
lua_register(L, "color", moduleColorCreate); jsDefineProperty(s_colorProto, "r", moduleColorGetR, moduleColorSetR);
lua_register(L, "colorRainbow", moduleColorRainbow); jsDefineProperty(s_colorProto, "g", moduleColorGetG, moduleColorSetG);
jsDefineProperty(s_colorProto, "b", moduleColorGetB, moduleColorSetB);
jsDefineProperty(s_colorProto, "a", moduleColorGetA, moduleColorSetA);
luaL_dostring(L, COLOR_SCRIPT); jsRegister("color", moduleColorCreate);
jsRegister("colorRainbow", moduleColorRainbow);
jsEvalStr(COLOR_SCRIPT);
} }
+38 -22
View File
@@ -8,35 +8,51 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "display/screen/screen.h" #include "display/screen/screen.h"
#include "display/color.h"
static int moduleScreenGetWidth(lua_State *L) { JS_FUNC(moduleScreenGetWidth) {
assertNotNull(L, "Lua state is null"); return jerry_number(SCREEN.width);
lua_pushnumber(L, SCREEN.width);
return 1;
} }
static int moduleScreenGetHeight(lua_State *L) { JS_FUNC(moduleScreenGetHeight) {
assertNotNull(L, "Lua state is null"); return jerry_number(SCREEN.height);
lua_pushnumber(L, SCREEN.height);
return 1;
} }
static int moduleScreenSetBackground(lua_State *L) { JS_FUNC(moduleScreenSetBackground) {
assertNotNull(L, "Lua state is null"); if(args_count < 1 || !jerry_value_is_object(args_p[0])) {
return JS_THROW("Screen background color must be a color object");
if(!lua_isuserdata(L, 1)) {
luaL_error(L, "Screen background color must be a color struct");
return 0;
} }
color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt"); jerry_value_t keyR = jerry_string_sz("r");
SCREEN.background = *color; jerry_value_t keyG = jerry_string_sz("g");
return 0; jerry_value_t keyB = jerry_string_sz("b");
jerry_value_t keyA = jerry_string_sz("a");
jerry_value_t valR = jerry_object_get(args_p[0], keyR);
jerry_value_t valG = jerry_object_get(args_p[0], keyG);
jerry_value_t valB = jerry_object_get(args_p[0], keyB);
jerry_value_t valA = jerry_object_get(args_p[0], keyA);
jerry_value_free(keyR);
jerry_value_free(keyG);
jerry_value_free(keyB);
jerry_value_free(keyA);
SCREEN.background.r = (colorchannel8_t)jerry_value_as_number(valR);
SCREEN.background.g = (colorchannel8_t)jerry_value_as_number(valG);
SCREEN.background.b = (colorchannel8_t)jerry_value_as_number(valB);
SCREEN.background.a = (colorchannel8_t)jerry_value_as_number(valA);
jerry_value_free(valR);
jerry_value_free(valG);
jerry_value_free(valB);
jerry_value_free(valA);
return jerry_undefined();
} }
static void moduleScreen(lua_State *L) { static void moduleScreen(void) {
assertNotNull(L, "Lua state cannot be NULL."); jsRegister("screenGetWidth", moduleScreenGetWidth);
lua_register(L, "screenGetWidth", moduleScreenGetWidth); jsRegister("screenGetHeight", moduleScreenGetHeight);
lua_register(L, "screenGetHeight", moduleScreenGetHeight); jsRegister("screenSetBackground", moduleScreenSetBackground);
lua_register(L, "screenSetBackground", moduleScreenSetBackground);
} }
+58 -51
View File
@@ -11,80 +11,87 @@
#include "display/shader/shaderunlit.h" #include "display/shader/shaderunlit.h"
#include "script/module/math/modulemat4.h" #include "script/module/math/modulemat4.h"
static int moduleShaderBind(lua_State *l) { // ---------------------------------------------------------------------------
assertNotNull(l, "Lua state cannot be NULL."); // Functions
shader_t *shader = (shader_t *)lua_touserdata(l, 1); // ---------------------------------------------------------------------------
assertNotNull(shader, "Shader pointer cannot be NULL.");
JS_FUNC(moduleShaderBind) {
JS_REQUIRE_ARGS(1);
shader_t *shader = (shader_t *)jsUnwrapPointer(args_p[0]);
if(!shader) return JS_THROW("shaderBind: expected a shader object");
errorret_t ret = shaderBind(shader); errorret_t ret = shaderBind(shader);
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
luaL_error(l, "Failed to bind shader: %s", ret.state->message);
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
return 0; return JS_THROW("shaderBind: failed to bind shader");
} }
return 0; return jerry_undefined();
} }
static int moduleShaderSetMatrix(lua_State *l) { JS_FUNC(moduleShaderSetMatrix) {
assertNotNull(l, "Lua state cannot be NULL."); JS_REQUIRE_ARGS(3);
shader_t *shader = (shader_t *)lua_touserdata(l, 1); shader_t *shader = (shader_t *)jsUnwrapPointer(args_p[0]);
assertNotNull(shader, "Shader pointer cannot be NULL."); if(!shader) return JS_THROW("shaderSetMatrix: expected a shader object");
const char_t *uniformName = luaL_checkstring(l, 2);
mat4 *mat = (mat4 *)luaL_checkudata(l, 3, "mat4_mt");
assertNotNull(mat, "Matrix pointer cannot be NULL.");
errorret_t ret = shaderSetMatrix(shader, uniformName, *mat); JS_REQUIRE_STRING(1);
char_t uniformName[128];
jsToString(args_p[1], uniformName, sizeof(uniformName));
mat4 mat;
if(!moduleMat4Check(args_p[2], mat)) {
return JS_THROW("shaderSetMatrix: third argument must be a mat4");
}
errorret_t ret = shaderSetMatrix(shader, uniformName, mat);
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
luaL_error(l, "Failed to set shader matrix: %s", ret.state->message);
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
return 0; return JS_THROW("shaderSetMatrix: failed to set shader matrix");
} }
return 0; return jerry_undefined();
} }
static int moduleShaderSetTexture(lua_State *l) { JS_FUNC(moduleShaderSetTexture) {
assertNotNull(l, "Lua state cannot be NULL."); JS_REQUIRE_ARGS(2);
shader_t *shader = (shader_t *)lua_touserdata(l, 1); shader_t *shader = (shader_t *)jsUnwrapPointer(args_p[0]);
assertNotNull(shader, "Shader pointer cannot be NULL."); if(!shader) return JS_THROW("shaderSetTexture: expected a shader object");
const char_t *uniformName = luaL_checkstring(l, 2);
assertStrLenMin(uniformName, 1, "Uniform name cannot be empty.");
texture_t *texture; JS_REQUIRE_STRING(1);
if(lua_isnil(l, 3)) { char_t uniformName[128];
texture = NULL; jsToString(args_p[1], uniformName, sizeof(uniformName));
} else if(lua_isuserdata(l, 3)) {
texture = (texture_t *)lua_touserdata(l, 3); texture_t *texture = NULL;
assertNotNull(texture, "Texture pointer cannot be NULL."); if(
} else { args_count >= 3 &&
luaL_error(l, "Third argument must be a texture_mt userdata or nil."); !jerry_value_is_null(args_p[2]) &&
return 0; !jerry_value_is_undefined(args_p[2])
) {
texture = (texture_t *)jsUnwrapPointer(args_p[2]);
if(!texture) return JS_THROW("shaderSetTexture: third argument must be a texture object or null");
} }
errorret_t ret = shaderSetTexture(shader, uniformName, texture); errorret_t ret = shaderSetTexture(shader, uniformName, texture);
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
luaL_error(l, "Failed to set shader texture: %s", ret.state->message);
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
return 0; return JS_THROW("shaderSetTexture: failed to set shader texture");
} }
return 0; return jerry_undefined();
} }
static void moduleShader(lua_State *L) { // ---------------------------------------------------------------------------
assertNotNull(L, "Lua state cannot be NULL."); // Module init
// ---------------------------------------------------------------------------
lua_pushlightuserdata(L, &SHADER_UNLIT); static void moduleShader(void) {
lua_setglobal(L, "SHADER_UNLIT"); jerry_value_t shaderUnlitVal = jsWrapPointer(&SHADER_UNLIT);
jsSetValue("SHADER_UNLIT", shaderUnlitVal);
jerry_value_free(shaderUnlitVal);
lua_pushstring(L, SHADER_UNLIT_PROJECTION); jsSetString("SHADER_UNLIT_PROJECTION", SHADER_UNLIT_PROJECTION);
lua_setglobal(L, "SHADER_UNLIT_PROJECTION"); jsSetString("SHADER_UNLIT_VIEW", SHADER_UNLIT_VIEW);
lua_pushstring(L, SHADER_UNLIT_VIEW); jsSetString("SHADER_UNLIT_MODEL", SHADER_UNLIT_MODEL);
lua_setglobal(L, "SHADER_UNLIT_VIEW"); jsSetString("SHADER_UNLIT_TEXTURE", SHADER_UNLIT_TEXTURE);
lua_pushstring(L, SHADER_UNLIT_MODEL);
lua_setglobal(L, "SHADER_UNLIT_MODEL");
lua_pushstring(L, SHADER_UNLIT_TEXTURE);
lua_setglobal(L, "SHADER_UNLIT_TEXTURE");
lua_register(L, "shaderBind", moduleShaderBind); jsRegister("shaderBind", moduleShaderBind);
lua_register(L, "shaderSetMatrix", moduleShaderSetMatrix); jsRegister("shaderSetMatrix", moduleShaderSetMatrix);
lua_register(L, "shaderSetTexture", moduleShaderSetTexture); jsRegister("shaderSetTexture", moduleShaderSetTexture);
} }
@@ -11,61 +11,98 @@
#include "script/module/math/modulevec2.h" #include "script/module/math/modulevec2.h"
#include "script/module/math/modulevec4.h" #include "script/module/math/modulevec4.h"
static int moduleSpriteBatchFlush(lua_State *L) { // ---------------------------------------------------------------------------
assertNotNull(L, "Lua state is null"); // Functions
// ---------------------------------------------------------------------------
JS_FUNC(moduleSpriteBatchFlush) {
spriteBatchFlush(); spriteBatchFlush();
return 0; return jerry_undefined();
} }
static int moduleSpriteBatchClear(lua_State *L) { JS_FUNC(moduleSpriteBatchClear) {
assertNotNull(L, "Lua state is null");
spriteBatchClear(); spriteBatchClear();
return 0; return jerry_undefined();
} }
static int moduleSpriteBatchPush(lua_State *L) { JS_FUNC(moduleSpriteBatchPush) {
assertNotNull(L, "Lua state is null"); JS_REQUIRE_ARGS(2);
vec2 min, max; vec2 minPos, maxPos;
moduleVec2Check(L, 1, min); if(!moduleVec2Check(args_p[0], minPos)) {
moduleVec2Check(L, 2, max); return JS_THROW("spriteBatchPush: first argument must be a vec2");
}
color_t *color = NULL; if(!moduleVec2Check(args_p[1], maxPos)) {
if(lua_gettop(L) < 3 || lua_isnil(L, 3)) { return JS_THROW("spriteBatchPush: second argument must be a vec2");
// allow NULL (white)
} else if(lua_isuserdata(L, 3)) {
color = (color_t *)luaL_checkudata(L, 3, "color_mt");
} else {
return luaL_error(L, "Sprite color must be a color struct or nil");
} }
#if MESH_ENABLE_COLOR
color_t col = COLOR_WHITE;
if(
args_count >= 3 &&
jerry_value_is_object(args_p[2]) &&
!jerry_value_is_null(args_p[2]) &&
!jerry_value_is_undefined(args_p[2])
) {
jerry_value_t kr = jerry_string_sz("r");
jerry_value_t vr = jerry_object_get(args_p[2], kr);
col.r = (colorchannel8_t)jerry_value_as_number(vr);
jerry_value_free(vr);
jerry_value_free(kr);
jerry_value_t kg = jerry_string_sz("g");
jerry_value_t vg = jerry_object_get(args_p[2], kg);
col.g = (colorchannel8_t)jerry_value_as_number(vg);
jerry_value_free(vg);
jerry_value_free(kg);
jerry_value_t kb = jerry_string_sz("b");
jerry_value_t vb = jerry_object_get(args_p[2], kb);
col.b = (colorchannel8_t)jerry_value_as_number(vb);
jerry_value_free(vb);
jerry_value_free(kb);
jerry_value_t ka = jerry_string_sz("a");
jerry_value_t va = jerry_object_get(args_p[2], ka);
col.a = (colorchannel8_t)jerry_value_as_number(va);
jerry_value_free(va);
jerry_value_free(ka);
}
#endif
float_t u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f; float_t u0 = 0.0f, v0 = 0.0f, u1 = 1.0f, v1 = 1.0f;
if(lua_gettop(L) >= 4 && !lua_isnil(L, 4)) { if(
vec4 uv; moduleVec4Check(L, 4, uv); args_count >= 4 &&
!jerry_value_is_null(args_p[3]) &&
!jerry_value_is_undefined(args_p[3])
) {
vec4 uv;
if(!moduleVec4Check(args_p[3], uv)) {
return JS_THROW("spriteBatchPush: fourth argument must be a vec4 or null");
}
u0 = uv[0]; v0 = uv[1]; u1 = uv[2]; v1 = uv[3]; u0 = uv[0]; v0 = uv[1]; u1 = uv[2]; v1 = uv[3];
} }
errorret_t ret = spriteBatchPush( errorret_t ret = spriteBatchPush(
min[0], min[1], max[0], max[1], minPos[0], minPos[1], maxPos[0], maxPos[1],
#if MESH_ENABLE_COLOR #if MESH_ENABLE_COLOR
color == NULL ? COLOR_WHITE : *color, col,
#endif #endif
u0, v0, u1, v1 u0, v0, u1, v1
); );
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
int err = luaL_error(L,
"Failed to push sprite to batch: %s",
ret.state->message
);
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
return err; return JS_THROW("spriteBatchPush: failed to push sprite to batch");
} }
return jerry_undefined();
return 0;
} }
static void moduleSpriteBatch(lua_State *L) { // ---------------------------------------------------------------------------
lua_register(L, "spriteBatchFlush", moduleSpriteBatchFlush); // Module init
lua_register(L, "spriteBatchClear", moduleSpriteBatchClear); // ---------------------------------------------------------------------------
lua_register(L, "spriteBatchPush", moduleSpriteBatchPush);
static void moduleSpriteBatch(void) {
jsRegister("spriteBatchFlush", moduleSpriteBatchFlush);
jsRegister("spriteBatchClear", moduleSpriteBatchClear);
jsRegister("spriteBatchPush", moduleSpriteBatchPush);
} }
+60 -30
View File
@@ -11,62 +11,92 @@
#include "display/spritebatch/spritebatch.h" #include "display/spritebatch/spritebatch.h"
#include "script/module/math/modulevec2.h" #include "script/module/math/modulevec2.h"
static int moduleTextDraw(lua_State *L) { // ---------------------------------------------------------------------------
assertNotNull(L, "Lua state is null"); // Functions
// ---------------------------------------------------------------------------
vec2 pos; moduleVec2Check(L, 1, pos); JS_FUNC(moduleTextDraw) {
JS_REQUIRE_ARGS(2);
if(!lua_isstring(L, 2)) { vec2 pos;
return luaL_error(L, "Text to draw must be a string"); if(!moduleVec2Check(args_p[0], pos)) {
return JS_THROW("textDraw: first argument must be a vec2 position");
} }
const char_t *text = (const char_t *)lua_tostring(L, 2);
color_t *color = NULL; JS_REQUIRE_STRING(1);
if(lua_gettop(L) < 3 || lua_isnil(L, 3)) { char_t text[1024];
// allow NULL (white) jsToString(args_p[1], text, sizeof(text));
} else if(lua_isuserdata(L, 3)) {
color = (color_t *)luaL_checkudata(L, 3, "color_mt"); color_t color = COLOR_WHITE;
} else { if(
return luaL_error(L, "Text color must be a color struct or nil"); args_count >= 3 &&
jerry_value_is_object(args_p[2]) &&
!jerry_value_is_null(args_p[2]) &&
!jerry_value_is_undefined(args_p[2])
) {
jerry_value_t kr = jerry_string_sz("r");
jerry_value_t vr = jerry_object_get(args_p[2], kr);
color.r = (colorchannel8_t)jerry_value_as_number(vr);
jerry_value_free(vr);
jerry_value_free(kr);
jerry_value_t kg = jerry_string_sz("g");
jerry_value_t vg = jerry_object_get(args_p[2], kg);
color.g = (colorchannel8_t)jerry_value_as_number(vg);
jerry_value_free(vg);
jerry_value_free(kg);
jerry_value_t kb = jerry_string_sz("b");
jerry_value_t vb = jerry_object_get(args_p[2], kb);
color.b = (colorchannel8_t)jerry_value_as_number(vb);
jerry_value_free(vb);
jerry_value_free(kb);
jerry_value_t ka = jerry_string_sz("a");
jerry_value_t va = jerry_object_get(args_p[2], ka);
color.a = (colorchannel8_t)jerry_value_as_number(va);
jerry_value_free(va);
jerry_value_free(ka);
} }
errorret_t ret = textDraw( errorret_t ret = textDraw(
pos[0], pos[1], text, pos[0], pos[1], text, color,
color == NULL ? COLOR_WHITE : *color,
&DEFAULT_FONT_TILESET, &DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE &DEFAULT_FONT_TEXTURE
); );
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
luaL_error(L, "Failed to draw text"); return JS_THROW("textDraw: failed to draw text");
} }
ret = spriteBatchFlush(); ret = spriteBatchFlush();
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
luaL_error(L, "Failed to flush sprite batch after drawing text"); return JS_THROW("textDraw: failed to flush sprite batch");
} }
return 0;
return jerry_undefined();
} }
static int moduleTextMeasure(lua_State *L) { JS_FUNC(moduleTextMeasure) {
assertNotNull(L, "Lua state is null"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_STRING(0);
if(!lua_isstring(L, 1)) { char_t text[1024];
return luaL_error(L, "Text to measure must be a string"); jsToString(args_p[0], text, sizeof(text));
}
const char_t *text = (const char_t *)lua_tostring(L, 1);
int32_t w = 0, h = 0; int32_t w = 0, h = 0;
textMeasure(text, &DEFAULT_FONT_TILESET, &w, &h); textMeasure(text, &DEFAULT_FONT_TILESET, &w, &h);
vec2 size = { (float_t)w, (float_t)h }; vec2 size = { (float_t)w, (float_t)h };
moduleVec2Push(L, size); return moduleVec2Push(size);
return 1;
} }
static void moduleText(lua_State *L) { // ---------------------------------------------------------------------------
assertNotNull(L, "Lua state is null"); // Module init
lua_register(L, "textDraw", moduleTextDraw); // ---------------------------------------------------------------------------
lua_register(L, "textMeasure", moduleTextMeasure);
static void moduleText(void) {
jsRegister("textDraw", moduleTextDraw);
jsRegister("textMeasure", moduleTextMeasure);
} }
+67 -61
View File
@@ -10,89 +10,95 @@
#include "display/texture/texture.h" #include "display/texture/texture.h"
#include "asset/loader/display/assettextureloader.h" #include "asset/loader/display/assettextureloader.h"
static int moduleTextureIndex(lua_State *l) { // ---------------------------------------------------------------------------
assertNotNull(l, "Lua state cannot be NULL."); // Native info / prototype
// ---------------------------------------------------------------------------
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt"); static void freeTextureNative(void *ptr, jerry_object_native_info_t *info) {
assertNotNull(tex, "Texture pointer cannot be NULL."); (void)info;
const char *key = luaL_checkstring(l, 2); textureDispose((texture_t *)ptr);
assertNotNull(key, "Key cannot be NULL."); free(ptr);
}
static const jerry_object_native_info_t TEXTURE_NATIVE_INFO = {
.free_cb = freeTextureNative,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_textureProto = 0;
if(stringCompare(key, "width") == 0) { // ---------------------------------------------------------------------------
lua_pushnumber(l, tex->width); return 1; // Helper
} else if(stringCompare(key, "height") == 0) { // ---------------------------------------------------------------------------
lua_pushnumber(l, tex->height); return 1;
}
lua_pushnil(l); /**
return 1; * Returns the native texture_t pointer stored in a JS value, or NULL.
*
* @param val JS value to inspect.
* @return Pointer to the texture_t, or NULL if not a texture object.
*/
static inline texture_t *moduleTextureGetNative(jerry_value_t val) {
return (texture_t *)jerry_object_get_native_ptr(val, &TEXTURE_NATIVE_INFO);
} }
static int moduleTextureToString(lua_State *l) { // ---------------------------------------------------------------------------
assertNotNull(l, "Lua state cannot be NULL."); // Property getters (read-only)
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt"); // ---------------------------------------------------------------------------
assertNotNull(tex, "Texture pointer cannot be NULL.");
char buffer[64]; JS_FUNC(moduleTextureGetWidth) {
snprintf(buffer, sizeof(buffer), "Texture(%dx%d)", tex->width, tex->height); texture_t *tex = (texture_t *)jerry_object_get_native_ptr(
lua_pushstring(l, buffer); call_info_p->this_value, &TEXTURE_NATIVE_INFO
return 1; );
return tex ? jerry_number(tex->width) : jerry_undefined();
} }
static int moduleTextureGC(lua_State *l) { JS_FUNC(moduleTextureGetHeight) {
assertNotNull(l, "Lua state cannot be NULL."); texture_t *tex = (texture_t *)jerry_object_get_native_ptr(
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt"); call_info_p->this_value, &TEXTURE_NATIVE_INFO
assertNotNull(tex, "Texture pointer cannot be NULL."); );
textureDispose(tex); return tex ? jerry_number(tex->height) : jerry_undefined();
return 0;
} }
static int moduleTextureLoad(lua_State *l) { // ---------------------------------------------------------------------------
assertNotNull(l, "Lua state cannot be NULL."); // Constructor
// ---------------------------------------------------------------------------
if(!lua_isstring(l, 1)) { JS_FUNC(moduleTextureLoad) {
luaL_error(l, "First argument must be a string filename."); JS_REQUIRE_ARGS(2);
return 0; JS_REQUIRE_STRING(0);
} JS_REQUIRE_NUMBER(1);
if(!lua_isnumber(l, 2)) {
luaL_error(l, "Second argument must be a number format.");
return 0;
}
const char_t *filename = lua_tostring(l, 1); char_t filename[256];
assertNotNull(filename, "Filename cannot be NULL."); jsToString(args_p[0], filename, sizeof(filename));
assertStrLenMin(filename, 1, "Filename cannot be empty.");
texture_t *tex = (texture_t *)lua_newuserdata(l, sizeof(texture_t)); textureformat_t format = (textureformat_t)(int32_t)jerry_value_as_number(args_p[1]);
texture_t *tex = (texture_t *)malloc(sizeof(texture_t));
memoryZero(tex, sizeof(texture_t)); memoryZero(tex, sizeof(texture_t));
textureformat_t format = (textureformat_t)lua_tonumber(l, 2);
errorret_t ret = assetTextureLoad(filename, tex, format); errorret_t ret = assetTextureLoad(filename, tex, format);
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
luaL_error(l, "Failed to load texture asset: %s", filename); free(tex);
return 0; return JS_THROW("textureLoad: failed to load texture");
} }
luaL_getmetatable(l, "texture_mt"); jerry_value_t obj = jerry_object();
lua_setmetatable(l, -2); jerry_object_set_native_ptr(obj, &TEXTURE_NATIVE_INFO, tex);
return 1; if(s_textureProto != 0) jerry_object_set_proto(obj, s_textureProto);
return obj;
} }
static void moduleTexture(lua_State *L) { // ---------------------------------------------------------------------------
assertNotNull(L, "Lua state cannot be null"); // Module init
// ---------------------------------------------------------------------------
if(luaL_newmetatable(L, "texture_mt")) { static void moduleTexture(void) {
lua_pushcfunction(L, moduleTextureIndex); s_textureProto = jerry_object();
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleTextureToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleTextureGC);
lua_setfield(L, -2, "__gc");
}
lua_pop(L, 1);
lua_pushnumber(L, TEXTURE_FORMAT_RGBA); jsDefineProperty(s_textureProto, "width", moduleTextureGetWidth, NULL);
lua_setglobal(L, "TEXTURE_FORMAT_RGBA"); jsDefineProperty(s_textureProto, "height", moduleTextureGetHeight, NULL);
lua_register(L, "textureLoad", moduleTextureLoad); jsSetInt("TEXTURE_FORMAT_RGBA", (int32_t)TEXTURE_FORMAT_RGBA);
jsRegister("textureLoad", moduleTextureLoad);
} }
+107 -72
View File
@@ -11,98 +11,133 @@
#include "asset/loader/display/assettilesetloader.h" #include "asset/loader/display/assettilesetloader.h"
#include "script/module/math/modulevec4.h" #include "script/module/math/modulevec4.h"
static int moduleTilesetIndex(lua_State *l) { // ---------------------------------------------------------------------------
assertNotNull(l, "Lua state cannot be NULL."); // Native info / prototype
const char_t *key = luaL_checkstring(l, 2); // ---------------------------------------------------------------------------
assertNotNull(key, "Key cannot be NULL.");
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
assertNotNull(ts, "Tileset pointer cannot be NULL.");
if(stringCompare(key, "tileWidth") == 0) { static void freeTilesetNative(void *ptr, jerry_object_native_info_t *info) {
lua_pushnumber(l, ts->tileWidth); return 1; (void)info;
} else if(stringCompare(key, "tileHeight") == 0) { free(ptr);
lua_pushnumber(l, ts->tileHeight); return 1;
} else if(stringCompare(key, "tileCount") == 0) {
lua_pushnumber(l, ts->tileCount); return 1;
} else if(stringCompare(key, "columns") == 0) {
lua_pushnumber(l, ts->columns); return 1;
} else if(stringCompare(key, "rows") == 0) {
lua_pushnumber(l, ts->rows); return 1;
}
lua_pushnil(l);
return 1;
} }
static const jerry_object_native_info_t TILESET_NATIVE_INFO = {
.free_cb = freeTilesetNative,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_tilesetProto = 0;
static int moduleTilesetToString(lua_State *l) { // ---------------------------------------------------------------------------
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt"); // Property getters (read-only)
assertNotNull(ts, "Tileset pointer cannot be NULL."); // ---------------------------------------------------------------------------
lua_pushfstring(l, "Tileset: %dx%d tile, %d columns, %d rows",
ts->tileWidth, ts->tileHeight, ts->columns, ts->rows JS_FUNC(moduleTilesetGetTileWidth) {
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TILESET_NATIVE_INFO
); );
return 1; return ts ? jerry_number(ts->tileWidth) : jerry_undefined();
} }
static int moduleTilesetTileGetUV(lua_State *l) { JS_FUNC(moduleTilesetGetTileHeight) {
assertNotNull(l, "Lua state cannot be NULL."); tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt"); call_info_p->this_value, &TILESET_NATIVE_INFO
assertNotNull(ts, "Tileset pointer cannot be NULL."); );
uint16_t tileIndex = (uint16_t)luaL_checknumber(l, 2); return ts ? jerry_number(ts->tileHeight) : jerry_undefined();
vec4 uv; tilesetTileGetUV(ts, tileIndex, uv);
moduleVec4Push(l, uv);
return 1;
} }
static int moduleTilesetPositionGetUV(lua_State *l) { JS_FUNC(moduleTilesetGetTileCount) {
assertNotNull(l, "Lua state cannot be NULL."); tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt"); call_info_p->this_value, &TILESET_NATIVE_INFO
assertNotNull(ts, "Tileset pointer cannot be NULL."); );
uint16_t column = (uint16_t)luaL_checknumber(l, 2); return ts ? jerry_number(ts->tileCount) : jerry_undefined();
uint16_t row = (uint16_t)luaL_checknumber(l, 3);
vec4 uv; tilesetPositionGetUV(ts, column, row, uv);
moduleVec4Push(l, uv);
return 1;
} }
static int moduleTilesetLoad(lua_State *l) { JS_FUNC(moduleTilesetGetColumns) {
assertNotNull(l, "Lua state cannot be NULL."); tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &TILESET_NATIVE_INFO
);
return ts ? jerry_number(ts->columns) : jerry_undefined();
}
if(!lua_isstring(l, 1)) { JS_FUNC(moduleTilesetGetRows) {
luaL_error(l, "First argument must be a string filename."); tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
return 0; call_info_p->this_value, &TILESET_NATIVE_INFO
} );
return ts ? jerry_number(ts->rows) : jerry_undefined();
}
const char_t *filename = lua_tostring(l, 1); // ---------------------------------------------------------------------------
assertNotNull(filename, "Filename cannot be NULL."); // Functions
assertStrLenMin(filename, 1, "Filename cannot be empty."); // ---------------------------------------------------------------------------
tileset_t *tileset = (tileset_t *)lua_newuserdata(l, sizeof(tileset_t)); JS_FUNC(modulesTilesetTileGetUV) {
memoryZero(tileset, sizeof(tileset_t)); JS_REQUIRE_ARGS(2);
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
args_p[0], &TILESET_NATIVE_INFO
);
if(!ts) return JS_THROW("tilesetTileGetUV: expected a tileset object");
errorret_t ret = assetTilesetLoad(filename, tileset); JS_REQUIRE_NUMBER(1);
uint16_t tileIndex = (uint16_t)jerry_value_as_number(args_p[1]);
vec4 uv;
tilesetTileGetUV(ts, tileIndex, uv);
return moduleVec4Push(uv);
}
JS_FUNC(moduleTilesetPositionGetUV) {
JS_REQUIRE_ARGS(3);
tileset_t *ts = (tileset_t *)jerry_object_get_native_ptr(
args_p[0], &TILESET_NATIVE_INFO
);
if(!ts) return JS_THROW("tilesetPositionGetUV: expected a tileset object");
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
uint16_t col = (uint16_t)jerry_value_as_number(args_p[1]);
uint16_t row = (uint16_t)jerry_value_as_number(args_p[2]);
vec4 uv;
tilesetPositionGetUV(ts, col, row, uv);
return moduleVec4Push(uv);
}
JS_FUNC(moduleTilesetLoad) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_STRING(0);
char_t filename[256];
jsToString(args_p[0], filename, sizeof(filename));
tileset_t *ts = (tileset_t *)malloc(sizeof(tileset_t));
memoryZero(ts, sizeof(tileset_t));
errorret_t ret = assetTilesetLoad(filename, ts);
if(ret.code != ERROR_OK) { if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret)); errorCatch(errorPrint(ret));
luaL_error(l, "Failed to load tileset asset: %s", filename); free(ts);
return 0; return JS_THROW("tilesetLoad: failed to load tileset");
} }
luaL_getmetatable(l, "tileset_mt"); jerry_value_t obj = jerry_object();
lua_setmetatable(l, -2); jerry_object_set_native_ptr(obj, &TILESET_NATIVE_INFO, ts);
return 1; if(s_tilesetProto != 0) jerry_object_set_proto(obj, s_tilesetProto);
return obj;
} }
static void moduleTileset(lua_State *L) { // ---------------------------------------------------------------------------
assertNotNull(L, "Lua state cannot be NULL"); // Module init
// ---------------------------------------------------------------------------
if(luaL_newmetatable(L, "tileset_mt")) { static void moduleTileset(void) {
lua_pushcfunction(L, moduleTilesetIndex); s_tilesetProto = jerry_object();
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleTilesetToString);
lua_setfield(L, -2, "__tostring");
}
lua_pop(L, 1);
lua_register(L, "tilesetLoad", moduleTilesetLoad); jsDefineProperty(s_tilesetProto, "tileWidth", moduleTilesetGetTileWidth, NULL);
lua_register(L, "tilesetTileGetUV", moduleTilesetTileGetUV); jsDefineProperty(s_tilesetProto, "tileHeight", moduleTilesetGetTileHeight, NULL);
lua_register(L, "tilesetPositionGetUV", moduleTilesetPositionGetUV); jsDefineProperty(s_tilesetProto, "tileCount", moduleTilesetGetTileCount, NULL);
jsDefineProperty(s_tilesetProto, "columns", moduleTilesetGetColumns, NULL);
jsDefineProperty(s_tilesetProto, "rows", moduleTilesetGetRows, NULL);
jsRegister("tilesetLoad", moduleTilesetLoad);
jsRegister("tilesetTileGetUV", modulesTilesetTileGetUV);
jsRegister("tilesetPositionGetUV", moduleTilesetPositionGetUV);
} }
@@ -9,102 +9,67 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "entity/entity.h" #include "entity/entity.h"
#include "entity/component/display/entitycamera.h" #include "entity/component/display/entitycamera.h"
#include "moduleentityposition.h"
typedef struct { // ---- camera prototype ----
entityid_t entityId;
componentid_t compId;
} entitycam_handle_t;
/** static jerry_value_t s_camProto = 0;
* __index metamethod for camera component userdata.
*
* @param L Lua state. Arg 1: entitycam userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityCameraIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt"); // -- zNear getter/setter --
assertNotNull(h, "invalid entitycam userdata");
const char *k = luaL_checkstring(L, 2); JS_FUNC(moduleEntityCameraGetZNear) {
assertStrLenMin(k, 1, "property key cannot be empty"); entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
if(stringCompare(k, "zNear") == 0) { return jerry_number((double)entityCameraGetZNear(eid, cid));
lua_pushnumber(L, entityCameraGetZNear(h->entityId, h->compId)); return 1;
} else if(stringCompare(k, "zFar") == 0) {
lua_pushnumber(L, entityCameraGetZFar(h->entityId, h->compId)); return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
} }
/** JS_FUNC(moduleEntityCameraSetZNear) {
* __newindex metamethod for camera component userdata. JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
* entityid_t eid = getEntityId(call_info_p->this_value);
* @param L Lua state. Arg 1: entitycam userdata. Arg 2: key string. Arg 3: number. componentid_t cid = getCompId(call_info_p->this_value);
* @return 0. entityCameraSetZNear(eid, cid, (float_t)jerry_value_as_number(args_p[0]));
*/ return jerry_undefined();
static int moduleEntityCameraNewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entitycam_handle_t *h = luaL_checkudata(L, 1, "entitycam_mt");
assertNotNull(h, "invalid entitycam userdata");
const char *k = luaL_checkstring(L, 2);
assertStrLenMin(k, 1, "property key cannot be empty");
if(stringCompare(k, "zNear") == 0) {
entityCameraSetZNear(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
} else if(stringCompare(k, "zFar") == 0) {
entityCameraSetZFar(h->entityId, h->compId, (float_t)luaL_checknumber(L, 3));
return 0;
}
luaL_error(L, "entitycam: unknown property '%s'", k);
return 0;
} }
// -- zFar getter/setter --
JS_FUNC(moduleEntityCameraGetZFar) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
return jerry_number((double)entityCameraGetZFar(eid, cid));
}
JS_FUNC(moduleEntityCameraSetZFar) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
entityCameraSetZFar(eid, cid, (float_t)jerry_value_as_number(args_p[0]));
return jerry_undefined();
}
// -- add function --
/** /**
* Adds a camera component to an entity and returns its userdata handle. * Adds a camera component to an entity and returns a handle object.
* * Arg 0: entity id (number).
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitycam userdata).
*/ */
static int moduleEntityCameraAdd(lua_State *L) { JS_FUNC(moduleEntityCameraAdd) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
entityid_t id = (entityid_t)luaL_checknumber(L, 1);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA); componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA);
jerry_value_t handle = makeEntityHandle(id, comp);
entitycam_handle_t *h = lua_newuserdata(L, sizeof(entitycam_handle_t)); if(s_camProto != 0) jerry_object_set_proto(handle, s_camProto);
assertNotNull(h, "failed to allocate entitycam userdata"); return handle;
h->entityId = id;
h->compId = comp;
luaL_getmetatable(L, "entitycam_mt");
lua_setmetatable(L, -2);
return 1;
} }
/** /**
* Registers the camera component metatable and entityCameraAdd global. * Registers the camera component prototype and entityCameraAdd global.
*
* @param L Lua state.
*/ */
static void moduleEntityCamera(lua_State *L) { static void moduleEntityCamera(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_camProto = jerry_object();
luaL_newmetatable(L, "entitycam_mt"); jsDefineProperty(s_camProto, "zNear", moduleEntityCameraGetZNear, moduleEntityCameraSetZNear);
lua_pushcfunction(L, moduleEntityCameraIndex); jsDefineProperty(s_camProto, "zFar", moduleEntityCameraGetZFar, moduleEntityCameraSetZFar);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityCameraNewIndex);
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_register(L, "entityCameraAdd", moduleEntityCameraAdd); jsRegister("entityCameraAdd", moduleEntityCameraAdd);
} }
@@ -9,73 +9,82 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "entity/entity.h" #include "entity/entity.h"
#include "entity/component/display/entitymaterial.h" #include "entity/component/display/entitymaterial.h"
#include "display/color.h"
#include "moduleentityposition.h"
typedef struct { // ---- material prototype ----
entityid_t entityId;
componentid_t compId; static jerry_value_t s_matProto = 0;
} entitymat_handle_t;
// -- methods --
/** /**
* __index metamethod for material component userdata. * Sets the material's unlit color from a color object with r, g, b, a
* Delegates all lookups to the metatable (methods only, no plain properties). * properties (each a number 0-255).
* * Args: colorObj (object).
* @param L Lua state. Arg 1: entitymat userdata. Arg 2: key string.
* @return 1.
*/ */
static int moduleEntityMaterialIndex(lua_State *L) { JS_FUNC(moduleEntityMaterialSetColor) {
lua_getmetatable(L, 1); JS_REQUIRE_ARGS(1);
lua_getfield(L, -1, luaL_checkstring(L, 2)); if(!jerry_value_is_object(args_p[0])) {
return 1; return JS_THROW("expected color object");
}
/**
* __newindex metamethod for material component userdata.
* Writes color (expects a color_mt userdata). Errors on unknown keys.
*
* @param L Lua state. Arg 1: entitymat userdata. Arg 2: key string. Arg 3: value.
* @return 0.
*/
static int moduleEntityMaterialNewIndex(lua_State *L) {
entitymat_handle_t *h = luaL_checkudata(L, 1, "entitymat_mt");
const char *k = luaL_checkstring(L, 2);
if(stringCompare(k, "color") == 0) {
const color_t *col = (const color_t *)luaL_checkudata(L, 3, "color_mt");
entityMaterialSetColor(h->entityId, h->compId, *col);
return 0;
} }
luaL_error(L, "entitymat: unknown property '%s'", k); entityid_t eid = getEntityId(call_info_p->this_value);
return 0; componentid_t cid = getCompId(call_info_p->this_value);
jerry_value_t key;
jerry_value_t v;
color_t col;
key = jerry_string_sz("r");
v = jerry_object_get(args_p[0], key);
jerry_value_free(key);
col.r = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
key = jerry_string_sz("g");
v = jerry_object_get(args_p[0], key);
jerry_value_free(key);
col.g = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
key = jerry_string_sz("b");
v = jerry_object_get(args_p[0], key);
jerry_value_free(key);
col.b = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
key = jerry_string_sz("a");
v = jerry_object_get(args_p[0], key);
jerry_value_free(key);
col.a = (colorchannel8_t)jerry_value_as_number(v);
jerry_value_free(v);
entityMaterialSetColor(eid, cid, col);
return jerry_undefined();
} }
// -- add function --
/** /**
* Adds a material component to an entity and returns its userdata handle. * Adds a material component to an entity and returns a handle object.
* * Arg 0: entity id (number).
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitymat userdata).
*/ */
static int moduleEntityMaterialAdd(lua_State *L) { JS_FUNC(moduleEntityMaterialAdd) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1); JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MATERIAL); componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MATERIAL);
entitymat_handle_t *h = lua_newuserdata(L, sizeof(entitymat_handle_t)); jerry_value_t handle = makeEntityHandle(id, comp);
h->entityId = id; if(s_matProto != 0) jerry_object_set_proto(handle, s_matProto);
h->compId = comp; return handle;
luaL_getmetatable(L, "entitymat_mt");
lua_setmetatable(L, -2);
return 1;
} }
/** /**
* Registers the material component metatable and entityMaterialAdd global. * Registers the material component prototype and entityMaterialAdd global.
*
* @param L Lua state.
*/ */
static void moduleEntityMaterial(lua_State *L) { static void moduleEntityMaterial(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_matProto = jerry_object();
luaL_newmetatable(L, "entitymat_mt"); jsDefineMethod(s_matProto, "setColor", moduleEntityMaterialSetColor);
lua_pushcfunction(L, moduleEntityMaterialIndex); lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleEntityMaterialNewIndex); lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
lua_register(L, "entityMaterialAdd", moduleEntityMaterialAdd); jsRegister("entityMaterialAdd", moduleEntityMaterialAdd);
} }
@@ -9,85 +9,65 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "entity/entity.h" #include "entity/entity.h"
#include "entity/component/display/entitymesh.h" #include "entity/component/display/entitymesh.h"
#include "moduleentityposition.h"
typedef struct { // ---- mesh prototype ----
entityid_t entityId;
componentid_t compId;
} entitymesh_handle_t;
/** static jerry_value_t s_meshProto = 0;
* __index metamethod for mesh component userdata.
* Delegates all lookups to the metatable (methods only, no plain properties). // -- methods --
*
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: key string.
* @return 1.
*/
static int moduleEntityMeshIndex(lua_State *L) {
lua_getmetatable(L, 1);
lua_getfield(L, -1, luaL_checkstring(L, 2));
return 1;
}
/** /**
* Generates an XZ-aligned plane mesh owned by this component. * Generates an XZ-aligned plane mesh owned by this component.
* * Args: width (number), depth (number).
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: width. Arg 3: depth.
* @return 0.
*/ */
static int moduleEntityMeshGeneratePlane(lua_State *L) { JS_FUNC(moduleEntityMeshGeneratePlane) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt"); JS_REQUIRE_ARGS(2);
float_t w = (float_t)luaL_checknumber(L, 2); entityid_t eid = getEntityId(call_info_p->this_value);
float_t d = (float_t)luaL_checknumber(L, 3); componentid_t cid = getCompId(call_info_p->this_value);
errorret_t err = entityMeshGeneratePlane(h->entityId, h->compId, w, d); float_t w = (float_t)jerry_value_as_number(args_p[0]);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generatePlane failed"); float_t d = (float_t)jerry_value_as_number(args_p[1]);
return 0; entityMeshGeneratePlane(eid, cid, w, d);
return jerry_undefined();
} }
/** /**
* Generates a Y-axis capsule mesh owned by this component. * Generates a Y-axis capsule mesh owned by this component.
* * Args: radius (number), halfHeight (number).
* @param L Lua state. Arg 1: entitymesh userdata. Arg 2: radius. Arg 3: halfHeight.
* @return 0.
*/ */
static int moduleEntityMeshGenerateCapsule(lua_State *L) { JS_FUNC(moduleEntityMeshGenerateCapsule) {
entitymesh_handle_t *h = luaL_checkudata(L, 1, "entitymesh_mt"); JS_REQUIRE_ARGS(2);
float_t r = (float_t)luaL_checknumber(L, 2); entityid_t eid = getEntityId(call_info_p->this_value);
float_t hh = (float_t)luaL_checknumber(L, 3); componentid_t cid = getCompId(call_info_p->this_value);
errorret_t err = entityMeshGenerateCapsule(h->entityId, h->compId, r, hh); float_t r = (float_t)jerry_value_as_number(args_p[0]);
if(err.code != ERROR_OK) luaL_error(L, "entityMesh: generateCapsule failed"); float_t hh = (float_t)jerry_value_as_number(args_p[1]);
return 0; entityMeshGenerateCapsule(eid, cid, r, hh);
return jerry_undefined();
} }
// -- add function --
/** /**
* Adds a mesh component to an entity and returns its userdata handle. * Adds a mesh component to an entity and returns a handle object.
* * Arg 0: entity id (number).
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitymesh userdata).
*/ */
static int moduleEntityMeshAdd(lua_State *L) { JS_FUNC(moduleEntityMeshAdd) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1); JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MESH); componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MESH);
entitymesh_handle_t *h = lua_newuserdata(L, sizeof(entitymesh_handle_t)); jerry_value_t handle = makeEntityHandle(id, comp);
h->entityId = id; if(s_meshProto != 0) jerry_object_set_proto(handle, s_meshProto);
h->compId = comp; return handle;
luaL_getmetatable(L, "entitymesh_mt");
lua_setmetatable(L, -2);
return 1;
} }
/** /**
* Registers the mesh component metatable and entityMeshAdd global. * Registers the mesh component prototype and entityMeshAdd global.
*
* @param L Lua state.
*/ */
static void moduleEntityMesh(lua_State *L) { static void moduleEntityMesh(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_meshProto = jerry_object();
luaL_newmetatable(L, "entitymesh_mt"); jsDefineMethod(s_meshProto, "generatePlane", moduleEntityMeshGeneratePlane);
lua_pushcfunction(L, moduleEntityMeshIndex); lua_setfield(L, -2, "__index"); jsDefineMethod(s_meshProto, "generateCapsule", moduleEntityMeshGenerateCapsule);
lua_pushcfunction(L, moduleEntityMeshGeneratePlane); lua_setfield(L, -2, "generatePlane");
lua_pushcfunction(L, moduleEntityMeshGenerateCapsule); lua_setfield(L, -2, "generateCapsule");
lua_pop(L, 1);
lua_register(L, "entityMeshAdd", moduleEntityMeshAdd); jsRegister("entityMeshAdd", moduleEntityMeshAdd);
} }
@@ -9,210 +9,225 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "entity/entity.h" #include "entity/entity.h"
#include "entity/component/physics/entityphysics.h" #include "entity/component/physics/entityphysics.h"
#include "moduleentityposition.h"
typedef struct { // ---- physics prototype ----
entityid_t entityId;
componentid_t compId;
} entityphysics_handle_t;
/** static jerry_value_t s_physProto = 0;
* __index metamethod for physics component userdata.
* Reads velX, velY, velZ, onGround (bool), bodyType; falls through to the // -- velocity getters/setters --
* metatable for method lookups.
* JS_FUNC(moduleEntityPhysicsGetVelX) {
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: key string. entityid_t eid = getEntityId(call_info_p->this_value);
* @return 1. componentid_t cid = getCompId(call_info_p->this_value);
*/
static int moduleEntityPhysicsIndex(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v; vec3 v;
if(stringCompare(k, "velX") == 0) { entityPhysicsGetVelocity(eid, cid, v);
entityPhysicsGetVelocity(h->entityId, h->compId, v); return jerry_number(v[0]);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "velY") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "velZ") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "onGround") == 0) {
lua_pushboolean(L, entityPhysicsIsOnGround(h->entityId, h->compId));
return 1;
} else if(stringCompare(k, "bodyType") == 0) {
lua_pushinteger(L, (lua_Integer)entityPhysicsGetBodyType(h->entityId, h->compId));
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
} }
/** JS_FUNC(moduleEntityPhysicsSetVelX) {
* __newindex metamethod for physics component userdata. JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
* Writes velX, velY, velZ, bodyType. onGround is read-only. Errors on unknown keys. entityid_t eid = getEntityId(call_info_p->this_value);
* componentid_t cid = getCompId(call_info_p->this_value);
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: key string. Arg 3: value.
* @return 0.
*/
static int moduleEntityPhysicsNewIndex(lua_State *L) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt");
const char *k = luaL_checkstring(L, 2);
vec3 v; vec3 v;
if(stringCompare(k, "velX") == 0) { entityPhysicsGetVelocity(eid, cid, v);
entityPhysicsGetVelocity(h->entityId, h->compId, v); v[0] = (float_t)jerry_value_as_number(args_p[0]);
v[0] = (float_t)luaL_checknumber(L, 3); entityPhysicsSetVelocity(eid, cid, v);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0; return jerry_undefined();
} else if(stringCompare(k, "velY") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "velZ") == 0) {
entityPhysicsGetVelocity(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPhysicsSetVelocity(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "bodyType") == 0) {
entityPhysicsSetBodyType(
h->entityId, h->compId,
(physicsbodytype_t)luaL_checkinteger(L, 3)
);
return 0;
} else if(stringCompare(k, "onGround") == 0) {
luaL_error(L, "entityphysics: onGround is read-only");
return 0;
}
luaL_error(L, "entityphysics: unknown property '%s'", k);
return 0;
} }
JS_FUNC(moduleEntityPhysicsGetVelY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPhysicsSetVelY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPhysicsSetVelocity(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPhysicsGetVelZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPhysicsSetVelZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPhysicsGetVelocity(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPhysicsSetVelocity(eid, cid, v);
return jerry_undefined();
}
// -- onGround getter (read-only) --
JS_FUNC(moduleEntityPhysicsGetOnGround) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
return jerry_boolean(entityPhysicsIsOnGround(eid, cid));
}
// -- bodyType getter/setter --
JS_FUNC(moduleEntityPhysicsGetBodyType) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
return jerry_number((double)entityPhysicsGetBodyType(eid, cid));
}
JS_FUNC(moduleEntityPhysicsSetBodyType) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
entityPhysicsSetBodyType(
eid, cid,
(physicsbodytype_t)(int32_t)jerry_value_as_number(args_p[0])
);
return jerry_undefined();
}
// -- methods --
/** /**
* Applies an immediate velocity impulse to the physics body. * Applies an immediate velocity impulse to the physics body.
* * Args: x, y, z (numbers).
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: impulse x, y, z.
* @return 0.
*/ */
static int moduleEntityPhysicsApplyImpulse(lua_State *L) { JS_FUNC(moduleEntityPhysicsApplyImpulse) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); JS_REQUIRE_ARGS(3);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 impulse = { vec3 impulse = {
(float_t)luaL_checknumber(L, 2), (float_t)jerry_value_as_number(args_p[0]),
(float_t)luaL_checknumber(L, 3), (float_t)jerry_value_as_number(args_p[1]),
(float_t)luaL_checknumber(L, 4) (float_t)jerry_value_as_number(args_p[2])
}; };
entityPhysicsApplyImpulse(h->entityId, h->compId, impulse); entityPhysicsApplyImpulse(eid, cid, impulse);
return 0; return jerry_undefined();
} }
/** /**
* Sets the physics shape to an axis-aligned box. * Sets the physics shape to an axis-aligned box.
* * Args: halfX, halfY, halfZ (numbers).
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: half-extents x, y, z.
* @return 0.
*/ */
static int moduleEntityPhysicsSetShapeCube(lua_State *L) { JS_FUNC(moduleEntityPhysicsSetShapeCube) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); JS_REQUIRE_ARGS(3);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape; physicsshape_t shape;
shape.type = PHYSICS_SHAPE_CUBE; shape.type = PHYSICS_SHAPE_CUBE;
shape.data.cube.halfExtents[0] = (float_t)luaL_checknumber(L, 2); shape.data.cube.halfExtents[0] = (float_t)jerry_value_as_number(args_p[0]);
shape.data.cube.halfExtents[1] = (float_t)luaL_checknumber(L, 3); shape.data.cube.halfExtents[1] = (float_t)jerry_value_as_number(args_p[1]);
shape.data.cube.halfExtents[2] = (float_t)luaL_checknumber(L, 4); shape.data.cube.halfExtents[2] = (float_t)jerry_value_as_number(args_p[2]);
entityPhysicsSetShape(h->entityId, h->compId, shape); entityPhysicsSetShape(eid, cid, shape);
return 0; return jerry_undefined();
} }
/** /**
* Sets the physics shape to a sphere. * Sets the physics shape to a sphere.
* * Args: radius (number).
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: radius.
* @return 0.
*/ */
static int moduleEntityPhysicsSetShapeSphere(lua_State *L) { JS_FUNC(moduleEntityPhysicsSetShapeSphere) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); JS_REQUIRE_ARGS(1);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape; physicsshape_t shape;
shape.type = PHYSICS_SHAPE_SPHERE; shape.type = PHYSICS_SHAPE_SPHERE;
shape.data.sphere.radius = (float_t)luaL_checknumber(L, 2); shape.data.sphere.radius = (float_t)jerry_value_as_number(args_p[0]);
entityPhysicsSetShape(h->entityId, h->compId, shape); entityPhysicsSetShape(eid, cid, shape);
return 0; return jerry_undefined();
} }
/** /**
* Sets the physics shape to a Y-axis capsule. * Sets the physics shape to a Y-axis capsule.
* * Args: radius (number), halfHeight (number).
* @param L Lua state. Arg 1: entityphysics userdata. Arg 2: radius. Arg 3: halfHeight.
* @return 0.
*/ */
static int moduleEntityPhysicsSetShapeCapsule(lua_State *L) { JS_FUNC(moduleEntityPhysicsSetShapeCapsule) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); JS_REQUIRE_ARGS(2);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape; physicsshape_t shape;
shape.type = PHYSICS_SHAPE_CAPSULE; shape.type = PHYSICS_SHAPE_CAPSULE;
shape.data.capsule.radius = (float_t)luaL_checknumber(L, 2); shape.data.capsule.radius = (float_t)jerry_value_as_number(args_p[0]);
shape.data.capsule.halfHeight = (float_t)luaL_checknumber(L, 3); shape.data.capsule.halfHeight = (float_t)jerry_value_as_number(args_p[1]);
entityPhysicsSetShape(h->entityId, h->compId, shape); entityPhysicsSetShape(eid, cid, shape);
return 0; return jerry_undefined();
} }
/** /**
* Sets the physics shape to an infinite plane. * Sets the physics shape to an infinite plane.
* * Args: normalX, normalY, normalZ, distance (numbers).
* @param L Lua state. Arg 1: entityphysics userdata. Args 2-4: normal x, y, z.
* Arg 5: distance from origin.
* @return 0.
*/ */
static int moduleEntityPhysicsSetShapePlane(lua_State *L) { JS_FUNC(moduleEntityPhysicsSetShapePlane) {
entityphysics_handle_t *h = luaL_checkudata(L, 1, "entityphysics_mt"); JS_REQUIRE_ARGS(4);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
physicsshape_t shape; physicsshape_t shape;
shape.type = PHYSICS_SHAPE_PLANE; shape.type = PHYSICS_SHAPE_PLANE;
shape.data.plane.normal[0] = (float_t)luaL_checknumber(L, 2); shape.data.plane.normal[0] = (float_t)jerry_value_as_number(args_p[0]);
shape.data.plane.normal[1] = (float_t)luaL_checknumber(L, 3); shape.data.plane.normal[1] = (float_t)jerry_value_as_number(args_p[1]);
shape.data.plane.normal[2] = (float_t)luaL_checknumber(L, 4); shape.data.plane.normal[2] = (float_t)jerry_value_as_number(args_p[2]);
shape.data.plane.distance = (float_t)luaL_checknumber(L, 5); shape.data.plane.distance = (float_t)jerry_value_as_number(args_p[3]);
entityPhysicsSetShape(h->entityId, h->compId, shape); entityPhysicsSetShape(eid, cid, shape);
return 0; return jerry_undefined();
} }
// -- add function --
/** /**
* Adds a physics component to an entity and returns its userdata handle. * Adds a physics component to an entity and returns a handle object.
* * Arg 0: entity id (number).
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entityphysics userdata).
*/ */
static int moduleEntityPhysicsAdd(lua_State *L) { JS_FUNC(moduleEntityPhysicsAdd) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1); JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_PHYSICS); componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_PHYSICS);
entityphysics_handle_t *h = lua_newuserdata(L, sizeof(entityphysics_handle_t)); jerry_value_t handle = makeEntityHandle(id, comp);
h->entityId = id; if(s_physProto != 0) jerry_object_set_proto(handle, s_physProto);
h->compId = comp; return handle;
luaL_getmetatable(L, "entityphysics_mt");
lua_setmetatable(L, -2);
return 1;
} }
/** /**
* Registers the physics component metatable, entityPhysicsAdd global, and * Registers the physics component prototype, entityPhysicsAdd global,
* PHYSICS_BODY_* / PHYSICS_SHAPE_* integer constants. * and PHYSICS_BODY_* / PHYSICS_SHAPE_* integer constants.
*
* @param L Lua state.
*/ */
static void moduleEntityPhysics(lua_State *L) { static void moduleEntityPhysics(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_physProto = jerry_object();
luaL_newmetatable(L, "entityphysics_mt"); jsDefineProperty(s_physProto, "velX", moduleEntityPhysicsGetVelX, moduleEntityPhysicsSetVelX);
lua_pushcfunction(L, moduleEntityPhysicsIndex); lua_setfield(L, -2, "__index"); jsDefineProperty(s_physProto, "velY", moduleEntityPhysicsGetVelY, moduleEntityPhysicsSetVelY);
lua_pushcfunction(L, moduleEntityPhysicsNewIndex); lua_setfield(L, -2, "__newindex"); jsDefineProperty(s_physProto, "velZ", moduleEntityPhysicsGetVelZ, moduleEntityPhysicsSetVelZ);
lua_pushcfunction(L, moduleEntityPhysicsApplyImpulse); lua_setfield(L, -2, "applyImpulse"); jsDefineProperty(s_physProto, "onGround", moduleEntityPhysicsGetOnGround, NULL);
lua_pushcfunction(L, moduleEntityPhysicsSetShapeCube); lua_setfield(L, -2, "setShapeCube"); jsDefineProperty(s_physProto, "bodyType", moduleEntityPhysicsGetBodyType, moduleEntityPhysicsSetBodyType);
lua_pushcfunction(L, moduleEntityPhysicsSetShapeSphere); lua_setfield(L, -2, "setShapeSphere");
lua_pushcfunction(L, moduleEntityPhysicsSetShapeCapsule); lua_setfield(L, -2, "setShapeCapsule");
lua_pushcfunction(L, moduleEntityPhysicsSetShapePlane); lua_setfield(L, -2, "setShapePlane");
lua_pop(L, 1);
lua_register(L, "entityPhysicsAdd", moduleEntityPhysicsAdd); jsDefineMethod(s_physProto, "applyImpulse", moduleEntityPhysicsApplyImpulse);
jsDefineMethod(s_physProto, "setShapeCube", moduleEntityPhysicsSetShapeCube);
jsDefineMethod(s_physProto, "setShapeSphere", moduleEntityPhysicsSetShapeSphere);
jsDefineMethod(s_physProto, "setShapeCapsule", moduleEntityPhysicsSetShapeCapsule);
jsDefineMethod(s_physProto, "setShapePlane", moduleEntityPhysicsSetShapePlane);
lua_pushinteger(L, PHYSICS_BODY_STATIC); lua_setglobal(L, "PHYSICS_BODY_STATIC"); jsRegister("entityPhysicsAdd", moduleEntityPhysicsAdd);
lua_pushinteger(L, PHYSICS_BODY_DYNAMIC); lua_setglobal(L, "PHYSICS_BODY_DYNAMIC");
lua_pushinteger(L, PHYSICS_BODY_KINEMATIC); lua_setglobal(L, "PHYSICS_BODY_KINEMATIC");
lua_pushinteger(L, PHYSICS_SHAPE_CUBE); lua_setglobal(L, "PHYSICS_SHAPE_CUBE"); jsSetInt("PHYSICS_BODY_STATIC", PHYSICS_BODY_STATIC);
lua_pushinteger(L, PHYSICS_SHAPE_SPHERE); lua_setglobal(L, "PHYSICS_SHAPE_SPHERE"); jsSetInt("PHYSICS_BODY_DYNAMIC", PHYSICS_BODY_DYNAMIC);
lua_pushinteger(L, PHYSICS_SHAPE_CAPSULE); lua_setglobal(L, "PHYSICS_SHAPE_CAPSULE"); jsSetInt("PHYSICS_BODY_KINEMATIC", PHYSICS_BODY_KINEMATIC);
lua_pushinteger(L, PHYSICS_SHAPE_PLANE); lua_setglobal(L, "PHYSICS_SHAPE_PLANE");
jsSetInt("PHYSICS_SHAPE_CUBE", PHYSICS_SHAPE_CUBE);
jsSetInt("PHYSICS_SHAPE_SPHERE", PHYSICS_SHAPE_SPHERE);
jsSetInt("PHYSICS_SHAPE_CAPSULE", PHYSICS_SHAPE_CAPSULE);
jsSetInt("PHYSICS_SHAPE_PLANE", PHYSICS_SHAPE_PLANE);
} }
@@ -10,166 +10,304 @@
#include "entity/entity.h" #include "entity/entity.h"
#include "entity/component/display/entityposition.h" #include "entity/component/display/entityposition.h"
typedef struct { // ---- shared entity handle helpers (included once via this file) ----
entityid_t entityId;
componentid_t compId; #ifndef ENTITY_HANDLE_HELPERS
} entitypos_handle_t; #define ENTITY_HANDLE_HELPERS
/** /**
* __index metamethod for position component userdata. * Create a plain JS object carrying _eid and _cid number properties.
* Reads x, y, z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ; falls through to * The caller is responsible for freeing the returned value.
* the metatable for method lookups.
*
* @param L Lua state. Arg 1: entitypos userdata. Arg 2: key string.
* @return 1.
*/ */
static int moduleEntityPositionIndex(lua_State *L) { static inline jerry_value_t makeEntityHandle(
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt"); entityid_t eid,
const char *k = luaL_checkstring(L, 2); componentid_t cid
vec3 v; ) {
if(stringCompare(k, "x") == 0) { jerry_value_t obj = jerry_object();
entityPositionGetPosition(h->entityId, h->compId, v); jerry_value_t key;
lua_pushnumber(L, v[0]); return 1; jerry_value_t val;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v); key = jerry_string_sz("_eid");
lua_pushnumber(L, v[1]); return 1; val = jerry_number((double)eid);
} else if(stringCompare(k, "z") == 0) { jerry_object_set(obj, key, val);
entityPositionGetPosition(h->entityId, h->compId, v); jerry_value_free(val);
lua_pushnumber(L, v[2]); return 1; jerry_value_free(key);
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v); key = jerry_string_sz("_cid");
lua_pushnumber(L, v[0]); return 1; val = jerry_number((double)cid);
} else if(stringCompare(k, "rotY") == 0) { jerry_object_set(obj, key, val);
entityPositionGetRotation(h->entityId, h->compId, v); jerry_value_free(val);
lua_pushnumber(L, v[1]); return 1; jerry_value_free(key);
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v); return obj;
lua_pushnumber(L, v[2]); return 1;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[0]); return 1;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[1]); return 1;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
lua_pushnumber(L, v[2]); return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, k);
return 1;
} }
/** /**
* __newindex metamethod for position component userdata. * Extract the entity ID stored in a handle object's _eid property.
* Writes x, y, z, rotX, rotY, rotZ, scaleX, scaleY, scaleZ and rebuilds the
* transform. Errors on unknown keys.
*
* @param L Lua state. Arg 1: entitypos userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/ */
static int moduleEntityPositionNewIndex(lua_State *L) { static inline entityid_t getEntityId(jerry_value_t handle) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt"); jerry_value_t key = jerry_string_sz("_eid");
const char *k = luaL_checkstring(L, 2); jerry_value_t val = jerry_object_get(handle, key);
vec3 v; jerry_value_free(key);
if(stringCompare(k, "x") == 0) { entityid_t id = (entityid_t)jerry_value_as_number(val);
entityPositionGetPosition(h->entityId, h->compId, v); jerry_value_free(val);
v[0] = (float_t)luaL_checknumber(L, 3); return id;
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "y") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "z") == 0) {
entityPositionGetPosition(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetPosition(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotX") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotY") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "rotZ") == 0) {
entityPositionGetRotation(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetRotation(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleX") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[0] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleY") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[1] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
} else if(stringCompare(k, "scaleZ") == 0) {
entityPositionGetScale(h->entityId, h->compId, v);
v[2] = (float_t)luaL_checknumber(L, 3);
entityPositionSetScale(h->entityId, h->compId, v); return 0;
}
luaL_error(L, "entitypos: unknown property '%s'", k);
return 0;
} }
/**
* Extract the component ID stored in a handle object's _cid property.
*/
static inline componentid_t getCompId(jerry_value_t handle) {
jerry_value_t key = jerry_string_sz("_cid");
jerry_value_t val = jerry_object_get(handle, key);
jerry_value_free(key);
componentid_t id = (componentid_t)jerry_value_as_number(val);
jerry_value_free(val);
return id;
}
#endif /* ENTITY_HANDLE_HELPERS */
// ---- position prototype ----
static jerry_value_t s_posProto = 0;
// -- position getters/setters --
JS_FUNC(moduleEntityPositionGetX) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
return jerry_number(v[0]);
}
JS_FUNC(moduleEntityPositionSetX) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
v[0] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetPosition(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPositionSetY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetPosition(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPositionSetZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetPosition(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetPosition(eid, cid, v);
return jerry_undefined();
}
// -- rotation getters/setters --
JS_FUNC(moduleEntityPositionGetRotX) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
return jerry_number(v[0]);
}
JS_FUNC(moduleEntityPositionSetRotX) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
v[0] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetRotation(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetRotY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPositionSetRotY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetRotation(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetRotZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPositionSetRotZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetRotation(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetRotation(eid, cid, v);
return jerry_undefined();
}
// -- scale getters/setters --
JS_FUNC(moduleEntityPositionGetScaleX) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
return jerry_number(v[0]);
}
JS_FUNC(moduleEntityPositionSetScaleX) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
v[0] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetScale(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetScaleY) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
return jerry_number(v[1]);
}
JS_FUNC(moduleEntityPositionSetScaleY) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
v[1] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetScale(eid, cid, v);
return jerry_undefined();
}
JS_FUNC(moduleEntityPositionGetScaleZ) {
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
return jerry_number(v[2]);
}
JS_FUNC(moduleEntityPositionSetScaleZ) {
JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 v;
entityPositionGetScale(eid, cid, v);
v[2] = (float_t)jerry_value_as_number(args_p[0]);
entityPositionSetScale(eid, cid, v);
return jerry_undefined();
}
// -- lookAt method --
/** /**
* Rotates the entity to face a world-space target point. * Rotates the entity to face a world-space target point.
* An optional up vector may be provided (args 5-7); defaults to (0,1,0). * Args: target x, y, z [, up x, y, z]. Up defaults to (0,1,0).
*
* @param L Lua state. Arg 1: entitypos userdata. Args 2-4: target x, y, z.
* Args 5-7 (optional): up x, y, z.
* @return 0.
*/ */
static int moduleEntityPositionLookAt(lua_State *L) { JS_FUNC(moduleEntityPositionLookAt) {
entitypos_handle_t *h = luaL_checkudata(L, 1, "entitypos_mt"); JS_REQUIRE_ARGS(3);
entityid_t eid = getEntityId(call_info_p->this_value);
componentid_t cid = getCompId(call_info_p->this_value);
vec3 target = { vec3 target = {
(float_t)luaL_checknumber(L, 2), (float_t)jerry_value_as_number(args_p[0]),
(float_t)luaL_checknumber(L, 3), (float_t)jerry_value_as_number(args_p[1]),
(float_t)luaL_checknumber(L, 4) (float_t)jerry_value_as_number(args_p[2])
}; };
vec3 up = { 0.0f, 1.0f, 0.0f }; vec3 up = { 0.0f, 1.0f, 0.0f };
if(lua_gettop(L) >= 7) { if(args_count >= 6) {
up[0] = (float_t)luaL_checknumber(L, 5); up[0] = (float_t)jerry_value_as_number(args_p[3]);
up[1] = (float_t)luaL_checknumber(L, 6); up[1] = (float_t)jerry_value_as_number(args_p[4]);
up[2] = (float_t)luaL_checknumber(L, 7); up[2] = (float_t)jerry_value_as_number(args_p[5]);
} }
vec3 eye; vec3 eye;
entityPositionGetPosition(h->entityId, h->compId, eye); entityPositionGetPosition(eid, cid, eye);
entityPositionLookAt(h->entityId, h->compId, target, up, eye); entityPositionLookAt(eid, cid, target, up, eye);
return 0; return jerry_undefined();
} }
// -- add function --
/** /**
* Adds a position component to an entity and returns its userdata handle. * Adds a position component to an entity and returns a handle object.
* * Arg 0: entity id (number).
* @param L Lua state. Arg 1: entity id (number).
* @return 1 (entitypos userdata).
*/ */
static int moduleEntityPositionAdd(lua_State *L) { JS_FUNC(moduleEntityPositionAdd) {
entityid_t id = (entityid_t)luaL_checknumber(L, 1); JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
entityid_t id = (entityid_t)jerry_value_as_number(args_p[0]);
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_POSITION); componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_POSITION);
entitypos_handle_t *h = lua_newuserdata(L, sizeof(entitypos_handle_t)); jerry_value_t handle = makeEntityHandle(id, comp);
h->entityId = id; if(s_posProto != 0) jerry_object_set_proto(handle, s_posProto);
h->compId = comp; return handle;
luaL_getmetatable(L, "entitypos_mt");
lua_setmetatable(L, -2);
return 1;
} }
/** /**
* Registers the position component metatable and entityPositionAdd global. * Registers the position component prototype and entityPositionAdd global.
*
* @param L Lua state.
*/ */
static void moduleEntityPosition(lua_State *L) { static void moduleEntityPosition(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_posProto = jerry_object();
luaL_newmetatable(L, "entitypos_mt"); jsDefineProperty(s_posProto, "x", moduleEntityPositionGetX, moduleEntityPositionSetX);
lua_pushcfunction(L, moduleEntityPositionIndex); lua_setfield(L, -2, "__index"); jsDefineProperty(s_posProto, "y", moduleEntityPositionGetY, moduleEntityPositionSetY);
lua_pushcfunction(L, moduleEntityPositionNewIndex); lua_setfield(L, -2, "__newindex"); jsDefineProperty(s_posProto, "z", moduleEntityPositionGetZ, moduleEntityPositionSetZ);
lua_pushcfunction(L, moduleEntityPositionLookAt); lua_setfield(L, -2, "lookAt"); jsDefineProperty(s_posProto, "rotX", moduleEntityPositionGetRotX, moduleEntityPositionSetRotX);
lua_pop(L, 1); jsDefineProperty(s_posProto, "rotY", moduleEntityPositionGetRotY, moduleEntityPositionSetRotY);
jsDefineProperty(s_posProto, "rotZ", moduleEntityPositionGetRotZ, moduleEntityPositionSetRotZ);
jsDefineProperty(s_posProto, "scaleX", moduleEntityPositionGetScaleX, moduleEntityPositionSetScaleX);
jsDefineProperty(s_posProto, "scaleY", moduleEntityPositionGetScaleY, moduleEntityPositionSetScaleY);
jsDefineProperty(s_posProto, "scaleZ", moduleEntityPositionGetScaleZ, moduleEntityPositionSetScaleZ);
lua_register(L, "entityPositionAdd", moduleEntityPositionAdd); jsDefineMethod(s_posProto, "lookAt", moduleEntityPositionLookAt);
jsRegister("entityPositionAdd", moduleEntityPositionAdd);
} }
+52 -56
View File
@@ -15,87 +15,83 @@
#include "component/moduleentitymaterial.h" #include "component/moduleentitymaterial.h"
#include "component/moduleentityphysics.h" #include "component/moduleentityphysics.h"
// ---- component type constants script ----
#define X(enumName, type, field, init, dispose) \ #define X(enumName, type, field, init, dispose) \
"COMPONENT_TYPE_" #enumName " = \"" #field "\"\n" "var COMPONENT_TYPE_" #enumName " = \"" #field "\";\n"
static const char_t *COMPONENT_TYPE_SCRIPT = static const char_t *COMPONENT_TYPE_SCRIPT =
#include "entity/componentlist.h" #include "entity/componentlist.h"
; ;
#undef X #undef X
// ---- Entity base class script ----
static const char_t *ENTITY_SCRIPT = static const char_t *ENTITY_SCRIPT =
"Entity = {}\n" "var Entity = {};\n"
"Entity.__index = Entity\n" "Entity.POSITION = COMPONENT_TYPE_POSITION;\n"
"Entity.CAMERA = COMPONENT_TYPE_CAMERA;\n"
"Entity.MESH = COMPONENT_TYPE_MESH;\n"
"Entity.MATERIAL = COMPONENT_TYPE_MATERIAL;\n"
"Entity.PHYSICS = COMPONENT_TYPE_PHYSICS;\n"
"\n" "\n"
"Entity.POSITION = COMPONENT_TYPE_POSITION\n" "var _addFns = {};\n"
"Entity.CAMERA = COMPONENT_TYPE_CAMERA\n" "_addFns[COMPONENT_TYPE_POSITION] = entityPositionAdd;\n"
"Entity.MESH = COMPONENT_TYPE_MESH\n" "_addFns[COMPONENT_TYPE_CAMERA] = entityCameraAdd;\n"
"Entity.MATERIAL = COMPONENT_TYPE_MATERIAL\n" "_addFns[COMPONENT_TYPE_MESH] = entityMeshAdd;\n"
"Entity.PHYSICS = COMPONENT_TYPE_PHYSICS\n" "_addFns[COMPONENT_TYPE_MATERIAL] = entityMaterialAdd;\n"
"_addFns[COMPONENT_TYPE_PHYSICS] = entityPhysicsAdd;\n"
"\n" "\n"
"local _addFns = {\n" "Entity.create = function() {\n"
" [COMPONENT_TYPE_POSITION] = entityPositionAdd,\n" " var self = Object.create(Entity);\n"
" [COMPONENT_TYPE_CAMERA] = entityCameraAdd,\n" " self.id = entityAdd();\n"
" [COMPONENT_TYPE_MESH] = entityMeshAdd,\n" " return self;\n"
" [COMPONENT_TYPE_MATERIAL] = entityMaterialAdd,\n" "};\n"
" [COMPONENT_TYPE_PHYSICS] = entityPhysicsAdd,\n"
"}\n"
"\n" "\n"
"function Entity.new()\n" "Entity.add = function(componentType) {\n"
" return setmetatable({ id = entityAdd() }, Entity)\n" " var fn = _addFns[componentType];\n"
"end\n" " if(!fn) throw new Error('unknown component type: ' + String(componentType));\n"
" this[componentType] = fn(this.id);\n"
" return this[componentType];\n"
"};\n"
"\n" "\n"
"function Entity:add(componentType)\n" "Entity.dispose = function() {\n"
" local fn = _addFns[componentType]\n" " entityRemove(this.id);\n"
" if not fn then error('unknown component type: ' .. tostring(componentType)) end\n" "};\n"
" self[componentType] = fn(self.id)\n"
" return self[componentType]\n"
"end\n"
"\n"
"function Entity:dispose()\n"
" entityRemove(self.id)\n"
"end\n"
; ;
// ---- module functions ----
/** /**
* Allocates a new entity and pushes its id onto the Lua stack. * Allocates a new entity and returns its id as a JS number.
*
* @param L Lua state.
* @return 1 (entity id number).
*/ */
static int moduleEntityAdd(lua_State *L) { JS_FUNC(moduleEntityAdd) {
lua_pushnumber(L, (lua_Number)entityManagerAdd()); return jerry_number((double)entityManagerAdd());
return 1;
} }
/** /**
* Disposes the entity with the given id. * Disposes the entity with the given id.
* * Arg 0: entity id (number).
* @param L Lua state. Arg 1: entity id (number).
* @return 0.
*/ */
static int moduleEntityRemove(lua_State *L) { JS_FUNC(moduleEntityRemove) {
entityDispose((entityid_t)luaL_checknumber(L, 1)); JS_REQUIRE_ARGS(1); JS_REQUIRE_NUMBER(0);
return 0; entityDispose((entityid_t)jerry_value_as_number(args_p[0]));
return jerry_undefined();
} }
/** /**
* Registers all entity and component modules, component type constants, and * Registers all entity and component modules, component type constants, and
* the Entity base class into the Lua state. * the Entity base object into the JS realm.
*
* @param L Lua state.
*/ */
static void moduleEntity(lua_State *L) { static void moduleEntity(void) {
assertNotNull(L, "Lua state cannot be NULL"); moduleEntityPosition();
moduleEntityCamera();
moduleEntityMesh();
moduleEntityMaterial();
moduleEntityPhysics();
moduleEntityPosition(L); jsRegister("entityAdd", moduleEntityAdd);
moduleEntityCamera(L); jsRegister("entityRemove", moduleEntityRemove);
moduleEntityMesh(L);
moduleEntityMaterial(L);
moduleEntityPhysics(L);
lua_register(L, "entityAdd", moduleEntityAdd); jsEvalStr(COMPONENT_TYPE_SCRIPT);
lua_register(L, "entityRemove", moduleEntityRemove); jsEvalStr(ENTITY_SCRIPT);
luaL_dostring(L, COMPONENT_TYPE_SCRIPT);
luaL_dostring(L, ENTITY_SCRIPT);
} }
+21 -36
View File
@@ -9,50 +9,35 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "event/event.h" #include "event/event.h"
static int moduleEventSubscribe(lua_State *L) { JS_FUNC(moduleEventSubscribe) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(2);
JS_REQUIRE_OBJECT(0);
JS_REQUIRE_FUNCTION(1);
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L); event_t *event = (event_t *)jsUnwrapPointer(args_p[0]);
assertNotNull(context, "Script context cannot be NULL"); if(event == NULL) return JS_THROW("eventSubscribe: Expected event object");
if(!lua_islightuserdata(L, 1)) { jerry_value_t funcCopy = jerry_value_copy(args_p[1]);
luaL_error(L, "eventSubscribe: Expected event pointer as first argument"); eventsub_t id = eventSubscribeScriptContext(event, scriptContextCurrent, funcCopy);
return 0;
}
if(!lua_isfunction(L, 2)) { return jerry_number((double)id);
luaL_error(L, "eventSubscribe: Expected function as second argument");
return 0;
}
event_t *event = (event_t *)lua_touserdata(L, 1);
assertNotNull(event, "Event cannot be NULL");
eventsub_t id = eventSubscribeScriptContext(event, context, 2);
lua_pushnumber(L, id);
return 1;
} }
static int moduleEventUnsubscribe(lua_State *L) { JS_FUNC(moduleEventUnsubscribe) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(2);
JS_REQUIRE_OBJECT(0);
JS_REQUIRE_NUMBER(1);
if(!lua_islightuserdata(L, 1)) { event_t *event = (event_t *)jsUnwrapPointer(args_p[0]);
luaL_error(L, "eventUnsubscribe: Expected event pointer as first argument"); if(event == NULL) return JS_THROW("eventUnsubscribe: Expected event object");
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); eventsub_t id = (eventsub_t)jerry_value_as_number(args_p[1]);
assertNotNull(event, "Event cannot be NULL");
eventsub_t id = (eventsub_t)lua_tonumber(L, 2);
eventUnsubscribe(event, id); eventUnsubscribe(event, id);
return 0;
return jerry_undefined();
} }
static void moduleEvent(lua_State *L) { static void moduleEvent(void) {
lua_register(L, "eventSubscribe", moduleEventSubscribe); jsRegister("eventSubscribe", moduleEventSubscribe);
lua_register(L, "eventUnsubscribe", moduleEventUnsubscribe); jsRegister("eventUnsubscribe", moduleEventUnsubscribe);
} }
+73 -135
View File
@@ -1,5 +1,5 @@
/** /**
* Copyright (c) 2025 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
@@ -9,198 +9,136 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "input/input.h" #include "input/input.h"
static int moduleInputIndex(lua_State *l) { JS_FUNC(moduleInputBind) {
assertNotNull(l, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(2);
JS_REQUIRE_STRING(0);
JS_REQUIRE_NUMBER(1);
const char_t *key = luaL_checkstring(l, 2); char_t strBtn[128];
assertStrLenMin(key, 1, "Key cannot be empty."); jsToString(args_p[0], strBtn, sizeof(strBtn));
if(strBtn[0] == '\0') return JS_THROW("inputBind: Button name cannot be empty");
if(stringCompare(key, "action") == 0) {
const inputevent_t *event = (const inputevent_t*)lua_touserdata(l, 1);
if(event == NULL) {
luaL_error(l, "Expected input event as first argument");
return 0;
}
lua_pushnumber(l, event->action);
return 1;
}
lua_pushnil(l);
return 1;
}
static int moduleInputBind(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "inputBind: Expected button name as first argument");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "inputBind: Expected action ID as second argument");
return 0;
}
const char_t *strBtn = lua_tostring(L, 1);
const inputaction_t action = (inputaction_t)lua_tonumber(L, 2);
if(strBtn == NULL || strlen(strBtn) == 0) {
luaL_error(L, "inputBind: Button name cannot be NULL or empty");
return 0;
}
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[1]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputBind: Invalid action ID %d with str %s", action, strBtn); return JS_THROW("inputBind: Invalid action ID");
return 0;
} }
inputbutton_t btn = inputButtonGetByName(strBtn); inputbutton_t btn = inputButtonGetByName(strBtn);
if(btn.type == INPUT_BUTTON_TYPE_NONE) { if(btn.type == INPUT_BUTTON_TYPE_NONE) {
luaL_error(L, "inputBind: Invalid button name '%s'", strBtn); return JS_THROW("inputBind: Invalid button name");
return 0;
} }
inputBind(btn, action); inputBind(btn, action);
return 0; return jerry_undefined();
} }
static int moduleInputIsDown(lua_State *L) { JS_FUNC(moduleInputIsDown) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) {
luaL_error(L, "inputIsDown: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputIsDown: Invalid action ID %d", action); return JS_THROW("inputIsDown: Invalid action ID");
return 0;
} }
lua_pushboolean(L, inputIsDown(action)); return jerry_boolean(inputIsDown(action));
return 1;
} }
static int moduleInputPressed(lua_State *L) { JS_FUNC(moduleInputPressed) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) {
luaL_error(L, "inputPressed: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputPressed: Invalid action ID %d", action); return JS_THROW("inputPressed: Invalid action ID");
return 0;
} }
lua_pushboolean(L, inputPressed(action)); return jerry_boolean(inputPressed(action));
return 1;
} }
static int moduleInputReleased(lua_State *L) { JS_FUNC(moduleInputReleased) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) {
luaL_error(L, "inputReleased: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputReleased: Invalid action ID %d", action); return JS_THROW("inputReleased: Invalid action ID");
return 0;
} }
lua_pushboolean(L, inputReleased(action)); return jerry_boolean(inputReleased(action));
return 1;
} }
static int moduleInputGetValue(lua_State *L) { JS_FUNC(moduleInputGetValue) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) { const inputaction_t action = (inputaction_t)jerry_value_as_number(args_p[0]);
luaL_error(L, "inputGetValue: Expected action ID as first argument");
return 0;
}
const inputaction_t action = (inputaction_t)lua_tonumber(L, 1);
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) { if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputGetValue: Invalid action ID %d", action); return JS_THROW("inputGetValue: Invalid action ID");
return 0;
} }
lua_pushnumber(L, inputGetCurrentValue(action)); return jerry_number(inputGetCurrentValue(action));
return 1;
} }
static int moduleInputAxis(lua_State *L) { JS_FUNC(moduleInputAxis) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
if(!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) { const inputaction_t neg = (inputaction_t)jerry_value_as_number(args_p[0]);
luaL_error(L, "inputAxis: Expected two action IDs as arguments (neg, pos)"); const inputaction_t pos = (inputaction_t)jerry_value_as_number(args_p[1]);
return 0;
}
const inputaction_t neg = (inputaction_t)lua_tonumber(L, 1);
const inputaction_t pos = (inputaction_t)lua_tonumber(L, 2);
if(neg < INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) { if(neg < INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputAxis: Invalid negative action ID %d", neg); return JS_THROW("inputAxis: Invalid negative action ID");
return 0;
} }
if(pos < INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) { if(pos < INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) {
luaL_error(L, "inputAxis: Invalid positive action ID %d", pos); return JS_THROW("inputAxis: Invalid positive action ID");
return 0;
} }
lua_pushnumber(L, inputAxis(neg, pos)); return jerry_number(inputAxis(neg, pos));
return 1;
} }
static void moduleInput(lua_State *L) { JS_FUNC(moduleInputGetEventAction) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
luaL_dostring(L, INPUT_ACTION_SCRIPT); const inputevent_t *event = (const inputevent_t *)jsUnwrapPointer(args_p[0]);
if(event == NULL) return JS_THROW("inputGetEventAction: Expected input event object");
luaL_dostring(L, return jerry_number(event->action);
}
static void moduleInput(void) {
jsEvalStr(INPUT_ACTION_SCRIPT);
jsEvalStr(
"" ""
#ifdef DUSK_INPUT_KEYBOARD #ifdef DUSK_INPUT_KEYBOARD
"INPUT_KEYBOARD = true\n" "var INPUT_KEYBOARD = true;\n"
#endif #endif
#ifdef DUSK_INPUT_GAMEPAD #ifdef DUSK_INPUT_GAMEPAD
"INPUT_GAMEPAD = true\n" "var INPUT_GAMEPAD = true;\n"
#endif #endif
#ifdef DUSK_INPUT_POINTER #ifdef DUSK_INPUT_POINTER
"INPUT_POINTER = true\n" "var INPUT_POINTER = true;\n"
#endif #endif
#ifdef DUSK_INPUT_TOUCH #ifdef DUSK_INPUT_TOUCH
"INPUT_TOUCH = true\n" "var INPUT_TOUCH = true;\n"
#endif #endif
); );
if(luaL_newmetatable(L, "input_mt")) { jerry_value_t evPressed = jsWrapPointer(&INPUT.eventPressed);
lua_pushcfunction(L, moduleInputIndex); jsSetValue("INPUT_EVENT_PRESSED", evPressed);
lua_setfield(L, -2, "__index"); jerry_value_free(evPressed);
}
lua_pop(L, 1);
lua_pushlightuserdata(L, &INPUT.eventPressed); jerry_value_t evReleased = jsWrapPointer(&INPUT.eventReleased);
lua_setglobal(L, "INPUT_EVENT_PRESSED"); jsSetValue("INPUT_EVENT_RELEASED", evReleased);
jerry_value_free(evReleased);
lua_pushlightuserdata(L, &INPUT.eventReleased); jsRegister("inputBind", moduleInputBind);
lua_setglobal(L, "INPUT_EVENT_RELEASED"); jsRegister("inputIsDown", moduleInputIsDown);
jsRegister("inputPressed", moduleInputPressed);
lua_register(L, "inputBind", moduleInputBind); jsRegister("inputReleased", moduleInputReleased);
lua_register(L, "inputIsDown", moduleInputIsDown); jsRegister("inputGetValue", moduleInputGetValue);
lua_register(L, "inputPressed", moduleInputPressed); jsRegister("inputAxis", moduleInputAxis);
lua_register(L, "inputReleased", moduleInputReleased); jsRegister("inputGetEventAction", moduleInputGetEventAction);
lua_register(L, "inputGetValue", moduleInputGetValue);
lua_register(L, "inputAxis", moduleInputAxis);
} }
+39 -46
View File
@@ -9,61 +9,57 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "locale/localemanager.h" #include "locale/localemanager.h"
static int moduleLocaleGetText(lua_State *L) { #define MODULE_LOCALE_MAX_ARGS 16
if(!lua_isstring(L, 1)) {
luaL_error(L, "Expected message ID as first argument");
return 0;
}
const char_t *id = lua_tostring(L, 1); JS_FUNC(moduleLocaleGetText) {
if(id == NULL || strlen(id) == 0) { JS_REQUIRE_ARGS(1);
luaL_error(L, "Message ID cannot be NULL or empty"); JS_REQUIRE_STRING(0);
return 0;
} char_t id[256];
jsToString(args_p[0], id, sizeof(id));
if(id[0] == '\0') return JS_THROW("localeGetText: Message ID cannot be empty");
int32_t plural = 0; int32_t plural = 0;
int top = lua_gettop(L); jerry_length_t argStart = 1;
int argStart = 2;
if(top >= 2 && !lua_isnil(L, 2)) { if(args_count >= 2 && !jerry_value_is_undefined(args_p[1])) {
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "Expected plural as second argument"); return JS_THROW("localeGetText: Expected plural as second argument");
return 0;
} }
plural = (int32_t)lua_tonumber(L, 2); plural = (int32_t)jerry_value_as_number(args_p[1]);
if(plural < 0) { if(plural < 0) return JS_THROW("localeGetText: Plural cannot be negative");
luaL_error(L, "Plural cannot be negative"); argStart = 2;
return 0;
}
argStart = 3;
} }
size_t argCount = (top >= argStart) ? (size_t)(top - argStart + 1) : 0; size_t argCount = (args_count > argStart) ? (size_t)(args_count - argStart) : 0;
#define MODULE_LOCALE_MAX_ARGS 16
assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS];
if(argCount > MODULE_LOCALE_MAX_ARGS) { if(argCount > MODULE_LOCALE_MAX_ARGS) {
luaL_error(L, "Too many args (max %d)", MODULE_LOCALE_MAX_ARGS); return JS_THROW("localeGetText: Too many format arguments");
return 0;
} }
assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS];
char_t strBufs[MODULE_LOCALE_MAX_ARGS][128];
for(size_t i = 0; i < argCount; ++i) { for(size_t i = 0; i < argCount; ++i) {
int luaIndex = argStart + (int)i; jerry_value_t arg = args_p[argStart + i];
if(lua_isinteger(L, luaIndex)) {
argsStack[i].type = ASSET_LOCALE_ARG_INT; if(jerry_value_is_number(arg)) {
argsStack[i].intValue = (int32_t)lua_tonumber(L, luaIndex); double num = jerry_value_as_number(arg);
} else if(lua_isnumber(L, luaIndex)) { if(num == (double)(int32_t)num) {
argsStack[i].type = ASSET_LOCALE_ARG_FLOAT; argsStack[i].type = ASSET_LOCALE_ARG_INT;
argsStack[i].floatValue = lua_tonumber(L, luaIndex); argsStack[i].intValue = (int32_t)num;
} else if(lua_isstring(L, luaIndex)) { } else {
argsStack[i].type = ASSET_LOCALE_ARG_FLOAT;
argsStack[i].floatValue = (float_t)num;
}
} else if(jerry_value_is_string(arg)) {
jsToString(arg, strBufs[i], sizeof(strBufs[i]));
argsStack[i].type = ASSET_LOCALE_ARG_STRING; argsStack[i].type = ASSET_LOCALE_ARG_STRING;
argsStack[i].stringValue = lua_tostring(L, luaIndex); argsStack[i].stringValue = strBufs[i];
} else { } else {
luaL_error(L, "Unsupported localization argument type"); return JS_THROW("localeGetText: Unsupported argument type");
return 0;
} }
} }
#undef MODULE_LOCALE_MAX_ARGS
char_t buffer[1024]; char_t buffer[1024];
errorret_t err = localeManagerGetTextArgs( errorret_t err = localeManagerGetTextArgs(
@@ -71,15 +67,12 @@ static int moduleLocaleGetText(lua_State *L) {
); );
if(err.code != ERROR_OK) { if(err.code != ERROR_OK) {
errorCatch(errorPrint(err)); errorCatch(errorPrint(err));
luaL_error(L, "Failed to get localized text for ID '%s'", id); return JS_THROW("localeGetText: Failed to get localized text");
return 0;
} }
lua_pushstring(L, buffer); return jerry_string_sz(buffer);
return 1;
} }
static void moduleLocale(lua_State *L) { static void moduleLocale(void) {
assertNotNull(L, "Lua state cannot be NULL"); jsRegister("localeGetText", moduleLocaleGetText);
lua_register(L, "localeGetText", moduleLocaleGetText);
} }
+265 -276
View File
@@ -7,293 +7,282 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
#include "modulevec3.h" #include "modulevec3.h"
#include "modulevec4.h" #include "modulevec4.h"
/** // Native info for heap-allocated mat4 (float[4][4])
* Pushes a new mat4 userdata onto the Lua stack with the mat4_mt metatable. static void freeMat4Native(void *ptr, jerry_object_native_info_t *info) {
* (void)info;
* @param L Lua state. free(ptr);
* @param m Source matrix to copy. }
*/ static const jerry_object_native_info_t MAT4_NATIVE_INFO = {
static void moduleMat4Push(lua_State *L, mat4 m) { .free_cb = freeMat4Native,
assertNotNull(L, "Lua state cannot be NULL"); .number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_mat4Proto = 0;
mat4 *u = (mat4 *)lua_newuserdata(L, sizeof(mat4)); // ---------------------------------------------------------------------------
glm_mat4_copy(m, *u); // Methods
luaL_getmetatable(L, "mat4_mt"); // ---------------------------------------------------------------------------
lua_setmetatable(L, -2);
JS_FUNC(moduleMatMul) {
JS_REQUIRE_ARGS(1);
float_t (*a)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!a) return JS_THROW("mat4.mul: invalid this");
float_t (*b)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
args_p[0], &MAT4_NATIVE_INFO
);
if(!b) return JS_THROW("mat4.mul: argument must be a mat4");
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_mul(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
} }
/** JS_FUNC(moduleMatTranspose) {
* Reads a mat4 userdata from the given stack index into out. float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
* call_info_p->this_value, &MAT4_NATIVE_INFO
* @param L Lua state. );
* @param idx Stack index of the mat4 userdata. if(!m) return JS_THROW("mat4.transpose: invalid this");
* @param out Destination matrix. float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
*/ glm_mat4_transpose_to(m, r);
static void moduleMat4Check(lua_State *L, int idx, mat4 out) { jerry_value_t obj = jerry_object();
assertNotNull(L, "Lua state cannot be NULL"); jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
mat4 *m = (mat4 *)luaL_checkudata(L, idx, "mat4_mt"); return obj;
glm_mat4_copy(*m, out);
} }
/** JS_FUNC(moduleMatInverse) {
* __index metamethod for mat4 userdata. float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
* Delegates all lookups to the metatable (methods only). call_info_p->this_value, &MAT4_NATIVE_INFO
* );
* @param L Lua state. Arg 1: mat4 userdata. Arg 2: key string. if(!m) return JS_THROW("mat4.inverse: invalid this");
* @return 1. float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
*/ glm_mat4_inv(m, r);
static int moduleMat4Index(lua_State *L) { jerry_value_t obj = jerry_object();
assertNotNull(L, "Lua state cannot be NULL"); jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
lua_getmetatable(L, 1); return obj;
lua_getfield(L, -1, luaL_checkstring(L, 2));
lua_remove(L, -2);
return 1;
} }
/** JS_FUNC(moduleMatDeterminant) {
* __mul metamethod: returns a * b as a new mat4. float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
* call_info_p->this_value, &MAT4_NATIVE_INFO
* @param L Lua state. Arg 1: mat4. Arg 2: mat4. );
* @return 1 (new mat4). if(!m) return JS_THROW("mat4.determinant: invalid this");
*/ return jerry_number(glm_mat4_det(m));
static int moduleMat4OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *a = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(a, "invalid mat4 userdata");
mat4 *b = (mat4 *)luaL_checkudata(L, 2, "mat4_mt");
assertNotNull(b, "invalid mat4 userdata");
mat4 r;
glm_mat4_mul(*a, *b, r);
moduleMat4Push(L, r);
return 1;
} }
/** JS_FUNC(moduleMatMulVec3) {
* __tostring metamethod: returns a placeholder string. JS_REQUIRE_ARGS(1);
* float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: mat4. call_info_p->this_value, &MAT4_NATIVE_INFO
* @return 1 (string). );
*/ if(!m) return JS_THROW("mat4.mulVec3: invalid this");
static int moduleMat4OpToString(lua_State *L) { vec3 vin;
assertNotNull(L, "Lua state cannot be NULL"); if(!moduleVec3Check(args_p[0], vin)) {
return JS_THROW("mat4.mulVec3: first argument must be a vec3");
lua_pushstring(L, "mat4(...)");
return 1;
}
/**
* Returns the transpose of a mat4 as a new mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4Transpose(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
mat4 r;
glm_mat4_transpose_to(*m, r);
moduleMat4Push(L, r);
return 1;
}
/**
* Returns the inverse of a mat4 as a new mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (new mat4).
*/
static int moduleMat4Inverse(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
mat4 r;
glm_mat4_inv(*m, r);
moduleMat4Push(L, r);
return 1;
}
/**
* Multiplies a mat4 by a vec3, with an optional w component (default 1.0).
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3. Arg 3 (optional): number w.
* @return 1 (new vec3).
*/
static int moduleMat4MulVec3(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
float_t w = lua_gettop(L) >= 3 ? (float_t)luaL_checknumber(L, 3) : 1.0f;
vec3 r;
glm_mat4_mulv3(*m, v, w, r);
moduleVec3Push(L, r);
return 1;
}
/**
* Multiplies a mat4 by a vec4.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleMat4MulVec4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec4 v;
moduleVec4Check(L, 2, v);
vec4 r;
glm_mat4_mulv(*m, v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Returns a copy of a mat4 translated by a vec3.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3.
* @return 1 (new mat4).
*/
static int moduleMat4Translate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
mat4 r;
glm_mat4_copy(*m, r);
glm_translate(r, v);
moduleMat4Push(L, r);
return 1;
}
/**
* Returns a copy of a mat4 scaled by a vec3.
*
* @param L Lua state. Arg 1: mat4. Arg 2: vec3.
* @return 1 (new mat4).
*/
static int moduleMat4Scale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
vec3 v;
moduleVec3Check(L, 2, v);
mat4 r;
glm_mat4_copy(*m, r);
glm_scale(r, v);
moduleMat4Push(L, r);
return 1;
}
/**
* Returns a new identity mat4.
*
* @param L Lua state.
* @return 1 (new mat4).
*/
static int moduleMat4Identity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 r;
glm_mat4_identity(r);
moduleMat4Push(L, r);
return 1;
}
/**
* Returns the determinant of a mat4.
*
* @param L Lua state. Arg 1: mat4.
* @return 1 (number).
*/
static int moduleMat4Determinant(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 *m = (mat4 *)luaL_checkudata(L, 1, "mat4_mt");
assertNotNull(m, "invalid mat4 userdata");
lua_pushnumber(L, (lua_Number)glm_mat4_det(*m));
return 1;
}
/**
* Constructor: creates a new identity mat4.
*
* @param L Lua state.
* @return 1 (new mat4).
*/
static int moduleMat4Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
mat4 m;
glm_mat4_identity(m);
moduleMat4Push(L, m);
return 1;
}
/**
* Registers the mat4 metatable and mat4 constructor global.
*
* @param L Lua state.
*/
static void moduleMat4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "mat4_mt")) {
lua_pop(L, 1);
return;
} }
float_t w = (args_count >= 2 && jerry_value_is_number(args_p[1]))
lua_pushcfunction(L, moduleMat4Index); ? (float_t)jerry_value_as_number(args_p[1])
lua_setfield(L, -2, "__index"); : 1.0f;
lua_pushcfunction(L, moduleMat4OpMul); vec3 vout;
lua_setfield(L, -2, "__mul"); glm_mat4_mulv3(m, vin, w, vout);
lua_pushcfunction(L, moduleMat4OpToString); return moduleVec3Push(vout);
lua_setfield(L, -2, "__tostring"); }
lua_pushcfunction(L, moduleMat4Transpose);
lua_setfield(L, -2, "transpose"); JS_FUNC(moduleMatMulVec4) {
lua_pushcfunction(L, moduleMat4Inverse); JS_REQUIRE_ARGS(1);
lua_setfield(L, -2, "inverse"); float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
lua_pushcfunction(L, moduleMat4MulVec3); call_info_p->this_value, &MAT4_NATIVE_INFO
lua_setfield(L, -2, "mulVec3"); );
lua_pushcfunction(L, moduleMat4MulVec4); if(!m) return JS_THROW("mat4.mulVec4: invalid this");
lua_setfield(L, -2, "mulVec4"); vec4 vin;
lua_pushcfunction(L, moduleMat4Translate); if(!moduleVec4Check(args_p[0], vin)) {
lua_setfield(L, -2, "translate"); return JS_THROW("mat4.mulVec4: first argument must be a vec4");
lua_pushcfunction(L, moduleMat4Scale); }
lua_setfield(L, -2, "scale"); vec4 vout;
lua_pushcfunction(L, moduleMat4Identity); glm_mat4_mulv(m, vin, vout);
lua_setfield(L, -2, "identity"); return moduleVec4Push(vout);
lua_pushcfunction(L, moduleMat4Determinant); }
lua_setfield(L, -2, "determinant");
lua_pop(L, 1); JS_FUNC(moduleMatTranslate) {
JS_REQUIRE_ARGS(1);
lua_register(L, "mat4", moduleMat4Create); float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.translate: invalid this");
vec3 tv;
if(!moduleVec3Check(args_p[0], tv)) {
return JS_THROW("mat4.translate: argument must be a vec3");
}
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_copy(m, r);
glm_translate(r, tv);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
JS_FUNC(moduleMatScale) {
JS_REQUIRE_ARGS(1);
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
call_info_p->this_value, &MAT4_NATIVE_INFO
);
if(!m) return JS_THROW("mat4.scale: invalid this");
vec3 sv;
if(!moduleVec3Check(args_p[0], sv)) {
return JS_THROW("mat4.scale: argument must be a vec3");
}
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_copy(m, r);
glm_scale(r, sv);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
JS_FUNC(moduleMatIdentityMethod) {
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_identity(r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Global constructor / factory functions
// ---------------------------------------------------------------------------
/**
* mat4Identity() - returns a new identity matrix.
*/
JS_FUNC(moduleMatIdentity) {
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_identity(r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* mat4Perspective(fov, aspect, near, far) - returns a perspective projection
* matrix.
*/
JS_FUNC(moduleMatPerspective) {
JS_REQUIRE_ARGS(4);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
JS_REQUIRE_NUMBER(3);
float_t fov = (float_t)jerry_value_as_number(args_p[0]);
float_t aspect = (float_t)jerry_value_as_number(args_p[1]);
float_t znear = (float_t)jerry_value_as_number(args_p[2]);
float_t zfar = (float_t)jerry_value_as_number(args_p[3]);
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_perspective(fov, aspect, znear, zfar, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
/**
* mat4LookAt(eye, center, up) - returns a view matrix.
* eye, center, up are vec3 objects.
*/
JS_FUNC(moduleMatLookAt) {
JS_REQUIRE_ARGS(3);
vec3 eye, center, up;
if(!moduleVec3Check(args_p[0], eye)) {
return JS_THROW("mat4LookAt: first argument (eye) must be a vec3");
}
if(!moduleVec3Check(args_p[1], center)) {
return JS_THROW("mat4LookAt: second argument (center) must be a vec3");
}
if(!moduleVec3Check(args_p[2], up)) {
return JS_THROW("mat4LookAt: third argument (up) must be a vec3");
}
float_t (*r)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_lookat(eye, center, up, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: push a cglm mat4 as a new JS object
// ---------------------------------------------------------------------------
/**
* Wraps a copy of a cglm mat4 as a new JerryScript object.
*
* @param m Source mat4 (float[4][4]) to copy.
* @return Owned jerry_value_t with native ptr set.
*/
static inline jerry_value_t moduleMat4Push(float (*m)[4]) {
float_t (*copy)[4] = (float_t (*)[4])malloc(sizeof(mat4));
glm_mat4_copy(m, copy);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &MAT4_NATIVE_INFO, copy);
jerry_object_set_proto(obj, s_mat4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: extract mat4 from a JS object
// ---------------------------------------------------------------------------
/**
* Reads a JerryScript mat4 object into an existing mat4 (float[4][4]).
*
* @param val JS value to read from.
* @param out Destination mat4.
* @return true if val carries a valid mat4 native ptr.
*/
static inline bool_t moduleMat4Check(jerry_value_t val, float (*out)[4]) {
float_t (*m)[4] = (float_t (*)[4])jerry_object_get_native_ptr(
val, &MAT4_NATIVE_INFO
);
if(!m) return false;
glm_mat4_copy(m, out);
return true;
}
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/**
* Creates the mat4 prototype with all methods, then registers the global
* factory functions mat4Identity(), mat4Perspective(), and mat4LookAt().
*/
static void moduleMat4(void) {
s_mat4Proto = jerry_object();
jsDefineMethod(s_mat4Proto, "mul", moduleMatMul);
jsDefineMethod(s_mat4Proto, "transpose", moduleMatTranspose);
jsDefineMethod(s_mat4Proto, "inverse", moduleMatInverse);
jsDefineMethod(s_mat4Proto, "determinant", moduleMatDeterminant);
jsDefineMethod(s_mat4Proto, "mulVec3", moduleMatMulVec3);
jsDefineMethod(s_mat4Proto, "mulVec4", moduleMatMulVec4);
jsDefineMethod(s_mat4Proto, "translate", moduleMatTranslate);
jsDefineMethod(s_mat4Proto, "scale", moduleMatScale);
jsDefineMethod(s_mat4Proto, "identity", moduleMatIdentityMethod);
jsRegister("mat4Identity", moduleMatIdentity);
jsRegister("mat4Perspective", moduleMatPerspective);
jsRegister("mat4LookAt", moduleMatLookAt);
} }
+5 -9
View File
@@ -14,14 +14,10 @@
/** /**
* Registers all math modules: vec2, vec3, vec4, mat4. * Registers all math modules: vec2, vec3, vec4, mat4.
*
* @param L Lua state.
*/ */
static void moduleMath(lua_State *L) { static void moduleMath(void) {
assertNotNull(L, "Lua state cannot be NULL"); moduleVec2();
moduleVec3();
moduleVec2(L); moduleVec4();
moduleVec3(L); moduleMat4();
moduleVec4(L);
moduleMat4(L);
} }
+217 -391
View File
@@ -7,441 +7,267 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
/** // Native info for heap-allocated vec2 (float[2])
* Pushes a new vec2 userdata onto the Lua stack with the vec2_mt metatable. static void freeVec2Native(void *ptr, jerry_object_native_info_t *info) {
* (void)info;
* @param L Lua state. free(ptr);
* @param v Source vector to copy. }
*/ static const jerry_object_native_info_t VEC2_NATIVE_INFO = {
static void moduleVec2Push(lua_State *L, vec2 v) { .free_cb = freeVec2Native,
assertNotNull(L, "Lua state cannot be NULL"); .number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_vec2Proto = 0;
vec2 *u = (vec2 *)lua_newuserdata(L, sizeof(vec2)); // ---------------------------------------------------------------------------
glm_vec2_copy(v, *u); // Property getters / setters
luaL_getmetatable(L, "vec2_mt"); // ---------------------------------------------------------------------------
lua_setmetatable(L, -2);
JS_FUNC(moduleVec2GetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
return v ? jerry_number(v[0]) : jerry_undefined();
}
JS_FUNC(moduleVec2SetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC2_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
} }
/** JS_FUNC(moduleVec2GetY) {
* Reads a vec2 userdata from the given stack index into out. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC2_NATIVE_INFO
* @param L Lua state. );
* @param idx Stack index of the vec2 userdata. return v ? jerry_number(v[1]) : jerry_undefined();
* @param out Destination vector. }
*/ JS_FUNC(moduleVec2SetY) {
static void moduleVec2Check(lua_State *L, int idx, vec2 out) { float_t *v = (float_t *)jerry_object_get_native_ptr(
assertNotNull(L, "Lua state cannot be NULL"); call_info_p->this_value, &VEC2_NATIVE_INFO
);
vec2 *v = (vec2 *)luaL_checkudata(L, idx, "vec2_mt"); if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
glm_vec2_copy(*v, out); return jerry_undefined();
} }
/** // ---------------------------------------------------------------------------
* __index metamethod for vec2 userdata. // Methods
* Supports integer indices 1-2 and string keys x, y; falls through to the // ---------------------------------------------------------------------------
* metatable for method lookups.
*
* @param L Lua state. Arg 1: vec2 userdata. Arg 2: integer or string key.
* @return 1.
*/
static int moduleVec2Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); JS_FUNC(moduleVec2Dot) {
assertNotNull(v, "invalid vec2 userdata"); JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
const char *key = luaL_checkstring(L, 2); call_info_p->this_value, &VEC2_NATIVE_INFO
assertStrLenMin(key, 1, "property key cannot be empty"); );
if(!a) return JS_THROW("vec2.dot: invalid this");
if(lua_isnumber(L, 2)) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
lua_Integer i = lua_tointeger(L, 2); if(!b) return JS_THROW("vec2.dot: argument must be a vec2");
if(i >= 1 && i <= 2) { return jerry_number(glm_vec2_dot(a, b));
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringCompare(key, "x") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringCompare(key, "y") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
} }
/** JS_FUNC(moduleVec2Length) {
* __newindex metamethod for vec2 userdata. float_t *v = (float_t *)jerry_object_get_native_ptr(
* Writes x, y. Errors on unknown keys. call_info_p->this_value, &VEC2_NATIVE_INFO
* );
* @param L Lua state. Arg 1: vec2 userdata. Arg 2: key string. Arg 3: number. if(!v) return JS_THROW("vec2.length: invalid this");
* @return 0. return jerry_number(glm_vec2_norm(v));
*/
static int moduleVec2NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringCompare(key, "x") == 0) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "y") == 0) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec2: unknown property '%s'", key);
return 0;
} }
/** JS_FUNC(moduleVec2LengthSq) {
* __add metamethod: returns a + b as a new vec2. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC2_NATIVE_INFO
* @param L Lua state. Arg 1: vec2. Arg 2: vec2. );
* @return 1 (new vec2). if(!v) return JS_THROW("vec2.lengthSq: invalid this");
*/ return jerry_number(glm_vec2_norm2(v));
static int moduleVec2OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
vec2 r;
glm_vec2_add(*a, *b, r);
moduleVec2Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec2Normalize) {
* __sub metamethod: returns a - b as a new vec2. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC2_NATIVE_INFO
* @param L Lua state. Arg 1: vec2. Arg 2: vec2. );
* @return 1 (new vec2). if(!v) return JS_THROW("vec2.normalize: invalid this");
*/ float_t *r = (float_t *)malloc(sizeof(vec2));
static int moduleVec2OpSub(lua_State *L) { glm_vec2_normalize_to(v, r);
assertNotNull(L, "Lua state cannot be NULL"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); jerry_object_set_proto(obj, s_vec2Proto);
assertNotNull(a, "invalid vec2 userdata"); return obj;
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
vec2 r;
glm_vec2_sub(*a, *b, r);
moduleVec2Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec2Negate) {
* __mul metamethod: returns v * scalar or scalar * v as a new vec2. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC2_NATIVE_INFO
* @param L Lua state. Arg 1: vec2 or number. Arg 2: number or vec2. );
* @return 1 (new vec2). if(!v) return JS_THROW("vec2.negate: invalid this");
*/ float_t *r = (float_t *)malloc(sizeof(vec2));
static int moduleVec2OpMul(lua_State *L) { glm_vec2_negate_to(v, r);
assertNotNull(L, "Lua state cannot be NULL"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
vec2 r; jerry_object_set_proto(obj, s_vec2Proto);
if(lua_isnumber(L, 1)) { return obj;
float_t s = (float_t)lua_tonumber(L, 1);
vec2 *v = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
glm_vec2_scale(*v, s, r);
} else {
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec2_scale(*v, s, r);
}
moduleVec2Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec2Add) {
* __div metamethod: returns v / scalar as a new vec2. JS_REQUIRE_ARGS(1);
* float_t *a = (float_t *)jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: vec2. Arg 2: number. call_info_p->this_value, &VEC2_NATIVE_INFO
* @return 1 (new vec2). );
*/ if(!a) return JS_THROW("vec2.add: invalid this");
static int moduleVec2OpDiv(lua_State *L) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
assertNotNull(L, "Lua state cannot be NULL"); if(!b) return JS_THROW("vec2.add: argument must be a vec2");
float_t *r = (float_t *)malloc(sizeof(vec2));
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); glm_vec2_add(a, b, r);
assertNotNull(v, "invalid vec2 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
float_t s = (float_t)luaL_checknumber(L, 2); jerry_object_set_proto(obj, s_vec2Proto);
vec2 r; return obj;
glm_vec2_divs(*v, s, r);
moduleVec2Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec2Sub) {
* __unm metamethod: returns -v as a new vec2. JS_REQUIRE_ARGS(1);
* float_t *a = (float_t *)jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: vec2. call_info_p->this_value, &VEC2_NATIVE_INFO
* @return 1 (new vec2). );
*/ if(!a) return JS_THROW("vec2.sub: invalid this");
static int moduleVec2OpUnm(lua_State *L) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
assertNotNull(L, "Lua state cannot be NULL"); if(!b) return JS_THROW("vec2.sub: argument must be a vec2");
float_t *r = (float_t *)malloc(sizeof(vec2));
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); glm_vec2_sub(a, b, r);
assertNotNull(v, "invalid vec2 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
vec2 r; jerry_object_set_proto(obj, s_vec2Proto);
glm_vec2_negate_to(*v, r); return obj;
moduleVec2Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec2Scale) {
* __eq metamethod: component-wise equality test. JS_REQUIRE_ARGS(1);
* JS_REQUIRE_NUMBER(0);
* @param L Lua state. Arg 1: vec2. Arg 2: vec2. float_t *v = (float_t *)jerry_object_get_native_ptr(
* @return 1 (boolean). call_info_p->this_value, &VEC2_NATIVE_INFO
*/ );
static int moduleVec2OpEq(lua_State *L) { if(!v) return JS_THROW("vec2.scale: invalid this");
assertNotNull(L, "Lua state cannot be NULL"); float_t s = (float_t)jerry_value_as_number(args_p[0]);
float_t *r = (float_t *)malloc(sizeof(vec2));
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); glm_vec2_scale(v, s, r);
assertNotNull(a, "invalid vec2 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); jerry_object_set_proto(obj, s_vec2Proto);
assertNotNull(b, "invalid vec2 userdata"); return obj;
lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1]);
return 1;
} }
/** JS_FUNC(moduleVec2Lerp) {
* __tostring metamethod: returns a human-readable representation. JS_REQUIRE_ARGS(2);
* JS_REQUIRE_NUMBER(1);
* @param L Lua state. Arg 1: vec2. float_t *a = (float_t *)jerry_object_get_native_ptr(
* @return 1 (string). call_info_p->this_value, &VEC2_NATIVE_INFO
*/ );
static int moduleVec2OpToString(lua_State *L) { if(!a) return JS_THROW("vec2.lerp: invalid this");
assertNotNull(L, "Lua state cannot be NULL"); float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
if(!b) return JS_THROW("vec2.lerp: first argument must be a vec2");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); float_t t = (float_t)jerry_value_as_number(args_p[1]);
assertNotNull(v, "invalid vec2 userdata"); float_t *r = (float_t *)malloc(sizeof(vec2));
glm_vec2_lerp(a, b, t, r);
char buf[64]; jerry_value_t obj = jerry_object();
snprintf(buf, sizeof(buf), "vec2(%.3f, %.3f)", (*v)[0], (*v)[1]); jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, r);
lua_pushstring(L, buf); jerry_object_set_proto(obj, s_vec2Proto);
return 1; return obj;
} }
/** JS_FUNC(moduleVec2Distance) {
* Returns the dot product of two vec2 values. JS_REQUIRE_ARGS(1);
* float_t *a = (float_t *)jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: vec2. Arg 2: vec2. call_info_p->this_value, &VEC2_NATIVE_INFO
* @return 1 (number). );
*/ if(!a) return JS_THROW("vec2.distance: invalid this");
static int moduleVec2Dot(lua_State *L) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC2_NATIVE_INFO);
assertNotNull(L, "Lua state cannot be NULL"); if(!b) return JS_THROW("vec2.distance: argument must be a vec2");
return jerry_number(glm_vec2_distance(a, b));
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(a, "invalid vec2 userdata");
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_dot(*a, *b));
return 1;
} }
/** // ---------------------------------------------------------------------------
* Returns the length (magnitude) of a vec2. // Constructor
* // ---------------------------------------------------------------------------
* @param L Lua state. Arg 1: vec2.
* @return 1 (number).
*/
static int moduleVec2Length(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); JS_FUNC(moduleVec2Create) {
assertNotNull(v, "invalid vec2 userdata"); JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(0);
lua_pushnumber(L, (lua_Number)glm_vec2_norm(*v)); JS_REQUIRE_NUMBER(1);
return 1; float_t *v = (float_t *)malloc(sizeof(vec2));
v[0] = (float_t)jerry_value_as_number(args_p[0]);
v[1] = (float_t)jerry_value_as_number(args_p[1]);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, v);
jerry_object_set_proto(obj, s_vec2Proto);
return obj;
} }
/** // ---------------------------------------------------------------------------
* Returns the squared length of a vec2. // Helper: push a cglm vec2 as a new JS object
* // ---------------------------------------------------------------------------
* @param L Lua state. Arg 1: vec2.
* @return 1 (number).
*/
static int moduleVec2LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
lua_pushnumber(L, (lua_Number)glm_vec2_norm2(*v));
return 1;
}
/** /**
* Returns a normalized copy of a vec2. * Wraps a copy of a cglm vec2 as a new JerryScript object.
* *
* @param L Lua state. Arg 1: vec2. * @param v Source float[2] to copy.
* @return 1 (new vec2). * @return Owned jerry_value_t with native ptr set.
*/ */
static int moduleVec2Normalize(lua_State *L) { static inline jerry_value_t moduleVec2Push(const float_t *v) {
assertNotNull(L, "Lua state cannot be NULL"); float_t *copy = (float_t *)malloc(sizeof(vec2));
copy[0] = v[0];
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); copy[1] = v[1];
assertNotNull(v, "invalid vec2 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC2_NATIVE_INFO, copy);
vec2 r; jerry_object_set_proto(obj, s_vec2Proto);
glm_vec2_normalize_to(*v, r); return obj;
moduleVec2Push(L, r);
return 1;
} }
// ---------------------------------------------------------------------------
// Helper: extract vec2 from a JS object
// ---------------------------------------------------------------------------
/** /**
* Linearly interpolates between two vec2 values. * Reads a JerryScript vec2 object into an existing float[2].
* *
* @param L Lua state. Arg 1: vec2 a. Arg 2: vec2 b. Arg 3: number t. * @param val JS value to read from.
* @return 1 (new vec2). * @param out Destination float[2].
* @return true if val carries a valid vec2 native ptr.
*/ */
static int moduleVec2Lerp(lua_State *L) { static inline bool_t moduleVec2Check(jerry_value_t val, float_t *out) {
assertNotNull(L, "Lua state cannot be NULL"); float_t *v = (float_t *)jerry_object_get_native_ptr(val, &VEC2_NATIVE_INFO);
if(!v) return false;
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); out[0] = v[0];
assertNotNull(a, "invalid vec2 userdata"); out[1] = v[1];
return true;
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt");
assertNotNull(b, "invalid vec2 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec2 r;
glm_vec2_lerp(*a, *b, t, r);
moduleVec2Push(L, r);
return 1;
} }
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/** /**
* Returns the distance between two vec2 points. * Creates the vec2 prototype with x/y getter-setter properties and all
* * methods, then registers the global vec2() constructor.
* @param L Lua state. Arg 1: vec2 a. Arg 2: vec2 b.
* @return 1 (number).
*/ */
static int moduleVec2Distance(lua_State *L) { static void moduleVec2(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_vec2Proto = jerry_object();
vec2 *a = (vec2 *)luaL_checkudata(L, 1, "vec2_mt"); jsDefineProperty(s_vec2Proto, "x", moduleVec2GetX, moduleVec2SetX);
assertNotNull(a, "invalid vec2 userdata"); jsDefineProperty(s_vec2Proto, "y", moduleVec2GetY, moduleVec2SetY);
vec2 *b = (vec2 *)luaL_checkudata(L, 2, "vec2_mt"); jsDefineMethod(s_vec2Proto, "dot", moduleVec2Dot);
assertNotNull(b, "invalid vec2 userdata"); jsDefineMethod(s_vec2Proto, "length", moduleVec2Length);
jsDefineMethod(s_vec2Proto, "lengthSq", moduleVec2LengthSq);
jsDefineMethod(s_vec2Proto, "normalize", moduleVec2Normalize);
jsDefineMethod(s_vec2Proto, "negate", moduleVec2Negate);
jsDefineMethod(s_vec2Proto, "add", moduleVec2Add);
jsDefineMethod(s_vec2Proto, "sub", moduleVec2Sub);
jsDefineMethod(s_vec2Proto, "scale", moduleVec2Scale);
jsDefineMethod(s_vec2Proto, "lerp", moduleVec2Lerp);
jsDefineMethod(s_vec2Proto, "distance", moduleVec2Distance);
lua_pushnumber(L, (lua_Number)glm_vec2_distance(*a, *b)); jsRegister("vec2", moduleVec2Create);
return 1;
}
/**
* Returns a negated copy of a vec2.
*
* @param L Lua state. Arg 1: vec2.
* @return 1 (new vec2).
*/
static int moduleVec2Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 *v = (vec2 *)luaL_checkudata(L, 1, "vec2_mt");
assertNotNull(v, "invalid vec2 userdata");
vec2 r;
glm_vec2_negate_to(*v, r);
moduleVec2Push(L, r);
return 1;
}
/**
* Constructor: creates a vec2 from two optional numbers (defaults to 0).
*
* @param L Lua state. Arg 1 (optional): x. Arg 2 (optional): y.
* @return 1 (new vec2).
*/
static int moduleVec2Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec2 v = {0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
moduleVec2Push(L, v);
return 1;
}
/**
* Registers the vec2 metatable and vec2 constructor global.
*
* @param L Lua state.
*/
static void moduleVec2(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "vec2_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec2Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec2NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec2OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec2OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec2OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec2OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec2OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec2OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec2OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec2Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec2Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec2LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec2Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec2Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec2Distance);
lua_setfield(L, -2, "distance");
lua_pushcfunction(L, moduleVec2Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec2", moduleVec2Create);
} }
+250 -422
View File
@@ -7,475 +7,303 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
/** // Native info for heap-allocated vec3 (float[3])
* Pushes a new vec3 userdata onto the Lua stack with the vec3_mt metatable. static void freeVec3Native(void *ptr, jerry_object_native_info_t *info) {
* (void)info;
* @param L Lua state. free(ptr);
* @param v Source vector to copy. }
*/ static const jerry_object_native_info_t VEC3_NATIVE_INFO = {
static void moduleVec3Push(lua_State *L, vec3 v) { .free_cb = freeVec3Native,
assertNotNull(L, "Lua state cannot be NULL"); .number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_vec3Proto = 0;
vec3 *u = (vec3 *)lua_newuserdata(L, sizeof(vec3)); // ---------------------------------------------------------------------------
glm_vec3_copy(v, *u); // Property getters / setters
luaL_getmetatable(L, "vec3_mt"); // ---------------------------------------------------------------------------
lua_setmetatable(L, -2);
JS_FUNC(moduleVec3GetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
return v ? jerry_number(v[0]) : jerry_undefined();
}
JS_FUNC(moduleVec3SetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC3_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
} }
/** JS_FUNC(moduleVec3GetY) {
* Reads a vec3 userdata from the given stack index into out. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC3_NATIVE_INFO
* @param L Lua state. );
* @param idx Stack index of the vec3 userdata. return v ? jerry_number(v[1]) : jerry_undefined();
* @param out Destination vector. }
*/ JS_FUNC(moduleVec3SetY) {
static void moduleVec3Check(lua_State *L, int idx, vec3 out) { float_t *v = (float_t *)jerry_object_get_native_ptr(
assertNotNull(L, "Lua state cannot be NULL"); call_info_p->this_value, &VEC3_NATIVE_INFO
);
vec3 *v = (vec3 *)luaL_checkudata(L, idx, "vec3_mt"); if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
glm_vec3_copy(*v, out); return jerry_undefined();
} }
/** JS_FUNC(moduleVec3GetZ) {
* __index metamethod for vec3 userdata. float_t *v = (float_t *)jerry_object_get_native_ptr(
* Supports integer indices 1-3 and string keys x, y, z; falls through to the call_info_p->this_value, &VEC3_NATIVE_INFO
* metatable for method lookups. );
* return v ? jerry_number(v[2]) : jerry_undefined();
* @param L Lua state. Arg 1: vec3 userdata. Arg 2: integer or string key. }
* @return 1. JS_FUNC(moduleVec3SetZ) {
*/ float_t *v = (float_t *)jerry_object_get_native_ptr(
static int moduleVec3Index(lua_State *L) { call_info_p->this_value, &VEC3_NATIVE_INFO
assertNotNull(L, "Lua state cannot be NULL"); );
if(v && args_count > 0) v[2] = (float_t)jerry_value_as_number(args_p[0]);
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); return jerry_undefined();
assertNotNull(v, "invalid vec3 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 3) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringCompare(key, "x") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringCompare(key, "y") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
if(stringCompare(key, "z") == 0) {
lua_pushnumber(L, (lua_Number)(*v)[2]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
} }
/** // ---------------------------------------------------------------------------
* __newindex metamethod for vec3 userdata. // Methods
* Writes x, y, z. Errors on unknown keys. // ---------------------------------------------------------------------------
*
* @param L Lua state. Arg 1: vec3 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec3NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); JS_FUNC(moduleVec3Dot) {
assertNotNull(v, "invalid vec3 userdata"); JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
const char *key = luaL_checkstring(L, 2); call_info_p->this_value, &VEC3_NATIVE_INFO
assertStrLenMin(key, 1, "property key cannot be empty"); );
if(!a) return JS_THROW("vec3.dot: invalid this");
if(stringCompare(key, "x") == 0) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
(*v)[0] = (float_t)luaL_checknumber(L, 3); if(!b) return JS_THROW("vec3.dot: argument must be a vec3");
return 0; return jerry_number(glm_vec3_dot(a, b));
}
if(stringCompare(key, "y") == 0) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringCompare(key, "z") == 0) {
(*v)[2] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec3: unknown property '%s'", key);
return 0;
} }
/** JS_FUNC(moduleVec3Cross) {
* __add metamethod: returns a + b as a new vec3. JS_REQUIRE_ARGS(1);
* float_t *a = (float_t *)jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: vec3. Arg 2: vec3. call_info_p->this_value, &VEC3_NATIVE_INFO
* @return 1 (new vec3). );
*/ if(!a) return JS_THROW("vec3.cross: invalid this");
static int moduleVec3OpAdd(lua_State *L) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
assertNotNull(L, "Lua state cannot be NULL"); if(!b) return JS_THROW("vec3.cross: argument must be a vec3");
float_t *r = (float_t *)malloc(sizeof(vec3));
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); glm_vec3_cross(a, b, r);
assertNotNull(a, "invalid vec3 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); jerry_object_set_proto(obj, s_vec3Proto);
assertNotNull(b, "invalid vec3 userdata"); return obj;
vec3 r;
glm_vec3_add(*a, *b, r);
moduleVec3Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec3Length) {
* __sub metamethod: returns a - b as a new vec3. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC3_NATIVE_INFO
* @param L Lua state. Arg 1: vec3. Arg 2: vec3. );
* @return 1 (new vec3). if(!v) return JS_THROW("vec3.length: invalid this");
*/ return jerry_number(glm_vec3_norm(v));
static int moduleVec3OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(a, "invalid vec3 userdata");
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(b, "invalid vec3 userdata");
vec3 r;
glm_vec3_sub(*a, *b, r);
moduleVec3Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec3LengthSq) {
* __mul metamethod: returns v * scalar or scalar * v as a new vec3. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC3_NATIVE_INFO
* @param L Lua state. Arg 1: vec3 or number. Arg 2: number or vec3. );
* @return 1 (new vec3). if(!v) return JS_THROW("vec3.lengthSq: invalid this");
*/ return jerry_number(glm_vec3_norm2(v));
static int moduleVec3OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec3 *v = (vec3 *)luaL_checkudata(L, 2, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
glm_vec3_scale(*v, s, r);
} else {
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec3_scale(*v, s, r);
}
moduleVec3Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec3Normalize) {
* __div metamethod: returns v / scalar as a new vec3. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC3_NATIVE_INFO
* @param L Lua state. Arg 1: vec3. Arg 2: number. );
* @return 1 (new vec3). if(!v) return JS_THROW("vec3.normalize: invalid this");
*/ float_t *r = (float_t *)malloc(sizeof(vec3));
static int moduleVec3OpDiv(lua_State *L) { glm_vec3_normalize_to(v, r);
assertNotNull(L, "Lua state cannot be NULL"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); jerry_object_set_proto(obj, s_vec3Proto);
assertNotNull(v, "invalid vec3 userdata"); return obj;
float_t s = (float_t)luaL_checknumber(L, 2);
vec3 r;
glm_vec3_divs(*v, s, r);
moduleVec3Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec3Negate) {
* __unm metamethod: returns -v as a new vec3. float_t *v = (float_t *)jerry_object_get_native_ptr(
* call_info_p->this_value, &VEC3_NATIVE_INFO
* @param L Lua state. Arg 1: vec3. );
* @return 1 (new vec3). if(!v) return JS_THROW("vec3.negate: invalid this");
*/ float_t *r = (float_t *)malloc(sizeof(vec3));
static int moduleVec3OpUnm(lua_State *L) { glm_vec3_negate_to(v, r);
assertNotNull(L, "Lua state cannot be NULL"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); jerry_object_set_proto(obj, s_vec3Proto);
assertNotNull(v, "invalid vec3 userdata"); return obj;
vec3 r;
glm_vec3_negate_to(*v, r);
moduleVec3Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec3Add) {
* __eq metamethod: component-wise equality test. JS_REQUIRE_ARGS(1);
* float_t *a = (float_t *)jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: vec3. Arg 2: vec3. call_info_p->this_value, &VEC3_NATIVE_INFO
* @return 1 (boolean). );
*/ if(!a) return JS_THROW("vec3.add: invalid this");
static int moduleVec3OpEq(lua_State *L) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
assertNotNull(L, "Lua state cannot be NULL"); if(!b) return JS_THROW("vec3.add: argument must be a vec3");
float_t *r = (float_t *)malloc(sizeof(vec3));
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); glm_vec3_add(a, b, r);
assertNotNull(a, "invalid vec3 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); jerry_object_set_proto(obj, s_vec3Proto);
assertNotNull(b, "invalid vec3 userdata"); return obj;
lua_pushboolean(L, (*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] && (*a)[2] == (*b)[2]);
return 1;
} }
/** JS_FUNC(moduleVec3Sub) {
* __tostring metamethod: returns a human-readable representation. JS_REQUIRE_ARGS(1);
* float_t *a = (float_t *)jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: vec3. call_info_p->this_value, &VEC3_NATIVE_INFO
* @return 1 (string). );
*/ if(!a) return JS_THROW("vec3.sub: invalid this");
static int moduleVec3OpToString(lua_State *L) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
assertNotNull(L, "Lua state cannot be NULL"); if(!b) return JS_THROW("vec3.sub: argument must be a vec3");
float_t *r = (float_t *)malloc(sizeof(vec3));
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); glm_vec3_sub(a, b, r);
assertNotNull(v, "invalid vec3 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
char buf[80]; jerry_object_set_proto(obj, s_vec3Proto);
snprintf(buf, sizeof(buf), "vec3(%.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2]); return obj;
lua_pushstring(L, buf);
return 1;
} }
/** JS_FUNC(moduleVec3Scale) {
* Returns the dot product of two vec3 values. JS_REQUIRE_ARGS(1);
* JS_REQUIRE_NUMBER(0);
* @param L Lua state. Arg 1: vec3. Arg 2: vec3. float_t *v = (float_t *)jerry_object_get_native_ptr(
* @return 1 (number). call_info_p->this_value, &VEC3_NATIVE_INFO
*/ );
static int moduleVec3Dot(lua_State *L) { if(!v) return JS_THROW("vec3.scale: invalid this");
assertNotNull(L, "Lua state cannot be NULL"); float_t s = (float_t)jerry_value_as_number(args_p[0]);
float_t *r = (float_t *)malloc(sizeof(vec3));
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); glm_vec3_scale(v, s, r);
assertNotNull(a, "invalid vec3 userdata"); jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); jerry_object_set_proto(obj, s_vec3Proto);
assertNotNull(b, "invalid vec3 userdata"); return obj;
lua_pushnumber(L, (lua_Number)glm_vec3_dot(*a, *b));
return 1;
} }
/** JS_FUNC(moduleVec3Lerp) {
* Returns the cross product of two vec3 values as a new vec3. JS_REQUIRE_ARGS(2);
* JS_REQUIRE_NUMBER(1);
* @param L Lua state. Arg 1: vec3. Arg 2: vec3. float_t *a = (float_t *)jerry_object_get_native_ptr(
* @return 1 (new vec3). call_info_p->this_value, &VEC3_NATIVE_INFO
*/ );
static int moduleVec3Cross(lua_State *L) { if(!a) return JS_THROW("vec3.lerp: invalid this");
assertNotNull(L, "Lua state cannot be NULL"); float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
if(!b) return JS_THROW("vec3.lerp: first argument must be a vec3");
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); float_t t = (float_t)jerry_value_as_number(args_p[1]);
assertNotNull(a, "invalid vec3 userdata"); float_t *r = (float_t *)malloc(sizeof(vec3));
glm_vec3_lerp(a, b, t, r);
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); jerry_value_t obj = jerry_object();
assertNotNull(b, "invalid vec3 userdata"); jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec3Proto);
vec3 r; return obj;
glm_vec3_cross(*a, *b, r);
moduleVec3Push(L, r);
return 1;
} }
/** JS_FUNC(moduleVec3Distance) {
* Returns the length (magnitude) of a vec3. JS_REQUIRE_ARGS(1);
* float_t *a = (float_t *)jerry_object_get_native_ptr(
* @param L Lua state. Arg 1: vec3. call_info_p->this_value, &VEC3_NATIVE_INFO
* @return 1 (number). );
*/ if(!a) return JS_THROW("vec3.distance: invalid this");
static int moduleVec3Length(lua_State *L) { float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC3_NATIVE_INFO);
assertNotNull(L, "Lua state cannot be NULL"); if(!b) return JS_THROW("vec3.distance: argument must be a vec3");
return jerry_number(glm_vec3_distance(a, b));
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_norm(*v));
return 1;
} }
/** // ---------------------------------------------------------------------------
* Returns the squared length of a vec3. // Constructor
* // ---------------------------------------------------------------------------
* @param L Lua state. Arg 1: vec3.
* @return 1 (number).
*/
static int moduleVec3LengthSq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); JS_FUNC(moduleVec3Create) {
assertNotNull(v, "invalid vec3 userdata"); JS_REQUIRE_ARGS(3);
JS_REQUIRE_NUMBER(0);
lua_pushnumber(L, (lua_Number)glm_vec3_norm2(*v)); JS_REQUIRE_NUMBER(1);
return 1; JS_REQUIRE_NUMBER(2);
float_t *v = (float_t *)malloc(sizeof(vec3));
v[0] = (float_t)jerry_value_as_number(args_p[0]);
v[1] = (float_t)jerry_value_as_number(args_p[1]);
v[2] = (float_t)jerry_value_as_number(args_p[2]);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, v);
jerry_object_set_proto(obj, s_vec3Proto);
return obj;
} }
/** // ---------------------------------------------------------------------------
* Returns a normalized copy of a vec3. // Helper: push a cglm vec3 as a new JS object
* // ---------------------------------------------------------------------------
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/
static int moduleVec3Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt");
assertNotNull(v, "invalid vec3 userdata");
vec3 r;
glm_vec3_normalize_to(*v, r);
moduleVec3Push(L, r);
return 1;
}
/** /**
* Linearly interpolates between two vec3 values. * Wraps a copy of a cglm vec3 as a new JerryScript object.
* *
* @param L Lua state. Arg 1: vec3 a. Arg 2: vec3 b. Arg 3: number t. * @param v Source float[3] to copy.
* @return 1 (new vec3). * @return Owned jerry_value_t with native ptr set.
*/ */
static int moduleVec3Lerp(lua_State *L) { static inline jerry_value_t moduleVec3Push(const float_t *v) {
assertNotNull(L, "Lua state cannot be NULL"); float_t *copy = (float_t *)malloc(sizeof(vec3));
copy[0] = v[0];
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); copy[1] = v[1];
assertNotNull(a, "invalid vec3 userdata"); copy[2] = v[2];
jerry_value_t obj = jerry_object();
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); jerry_object_set_native_ptr(obj, &VEC3_NATIVE_INFO, copy);
assertNotNull(b, "invalid vec3 userdata"); jerry_object_set_proto(obj, s_vec3Proto);
return obj;
float_t t = (float_t)luaL_checknumber(L, 3);
vec3 r;
glm_vec3_lerp(*a, *b, t, r);
moduleVec3Push(L, r);
return 1;
} }
// ---------------------------------------------------------------------------
// Helper: extract vec3 from a JS object
// ---------------------------------------------------------------------------
/** /**
* Returns the distance between two vec3 points. * Reads a JerryScript vec3 object into an existing float[3].
* *
* @param L Lua state. Arg 1: vec3 a. Arg 2: vec3 b. * @param val JS value to read from.
* @return 1 (number). * @param out Destination float[3].
* @return true if val carries a valid vec3 native ptr.
*/ */
static int moduleVec3Distance(lua_State *L) { static inline bool_t moduleVec3Check(jerry_value_t val, float_t *out) {
assertNotNull(L, "Lua state cannot be NULL"); float_t *v = (float_t *)jerry_object_get_native_ptr(val, &VEC3_NATIVE_INFO);
if(!v) return false;
vec3 *a = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); out[0] = v[0];
assertNotNull(a, "invalid vec3 userdata"); out[1] = v[1];
out[2] = v[2];
vec3 *b = (vec3 *)luaL_checkudata(L, 2, "vec3_mt"); return true;
assertNotNull(b, "invalid vec3 userdata");
lua_pushnumber(L, (lua_Number)glm_vec3_distance(*a, *b));
return 1;
} }
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/** /**
* Returns a negated copy of a vec3. * Creates the vec3 prototype with x/y/z getter-setter properties and all
* * methods, then registers the global vec3() constructor.
* @param L Lua state. Arg 1: vec3.
* @return 1 (new vec3).
*/ */
static int moduleVec3Negate(lua_State *L) { static void moduleVec3(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_vec3Proto = jerry_object();
vec3 *v = (vec3 *)luaL_checkudata(L, 1, "vec3_mt"); jsDefineProperty(s_vec3Proto, "x", moduleVec3GetX, moduleVec3SetX);
assertNotNull(v, "invalid vec3 userdata"); jsDefineProperty(s_vec3Proto, "y", moduleVec3GetY, moduleVec3SetY);
jsDefineProperty(s_vec3Proto, "z", moduleVec3GetZ, moduleVec3SetZ);
vec3 r; jsDefineMethod(s_vec3Proto, "dot", moduleVec3Dot);
glm_vec3_negate_to(*v, r); jsDefineMethod(s_vec3Proto, "cross", moduleVec3Cross);
moduleVec3Push(L, r); jsDefineMethod(s_vec3Proto, "length", moduleVec3Length);
return 1; jsDefineMethod(s_vec3Proto, "lengthSq", moduleVec3LengthSq);
} jsDefineMethod(s_vec3Proto, "normalize", moduleVec3Normalize);
jsDefineMethod(s_vec3Proto, "negate", moduleVec3Negate);
/** jsDefineMethod(s_vec3Proto, "add", moduleVec3Add);
* Constructor: creates a vec3 from three optional numbers (defaults to 0). jsDefineMethod(s_vec3Proto, "sub", moduleVec3Sub);
* jsDefineMethod(s_vec3Proto, "scale", moduleVec3Scale);
* @param L Lua state. Arg 1 (optional): x. Arg 2 (optional): y. Arg 3 (optional): z. jsDefineMethod(s_vec3Proto, "lerp", moduleVec3Lerp);
* @return 1 (new vec3). jsDefineMethod(s_vec3Proto, "distance", moduleVec3Distance);
*/
static int moduleVec3Create(lua_State *L) { jsRegister("vec3", moduleVec3Create);
assertNotNull(L, "Lua state cannot be NULL");
vec3 v = {0, 0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
if(top >= 3) v[2] = (float_t)luaL_checknumber(L, 3);
moduleVec3Push(L, v);
return 1;
}
/**
* Registers the vec3 metatable and vec3 constructor global.
*
* @param L Lua state.
*/
static void moduleVec3(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "vec3_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec3Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec3NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec3OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec3OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec3OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec3OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec3OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec3OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec3OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec3Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec3Cross);
lua_setfield(L, -2, "cross");
lua_pushcfunction(L, moduleVec3Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec3LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec3Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec3Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec3Distance);
lua_setfield(L, -2, "distance");
lua_pushcfunction(L, moduleVec3Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec3", moduleVec3Create);
} }
+333 -420
View File
@@ -7,445 +7,358 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "assert/assert.h"
#include "cglm/cglm.h"
/** // Native info for heap-allocated vec4 (float[4])
* Pushes a new vec4 userdata onto the Lua stack with the vec4_mt metatable. static void freeVec4Native(void *ptr, jerry_object_native_info_t *info) {
* (void)info;
* @param L Lua state. free(ptr);
* @param v Source vector to copy.
*/
static void moduleVec4Push(lua_State *L, vec4 v) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *u = (vec4 *)lua_newuserdata(L, sizeof(vec4));
glm_vec4_copy(v, *u);
luaL_getmetatable(L, "vec4_mt");
lua_setmetatable(L, -2);
} }
static const jerry_object_native_info_t VEC4_NATIVE_INFO = {
.free_cb = freeVec4Native,
.number_of_references = 0,
.offset_of_references = 0
};
static jerry_value_t s_vec4Proto = 0;
/** // ---------------------------------------------------------------------------
* Reads a vec4 userdata from the given stack index into out. // Property getters / setters (x/u0, y/v0, z/u1, w/v1)
* // ---------------------------------------------------------------------------
* @param L Lua state.
* @param idx Stack index of the vec4 userdata.
* @param out Destination vector.
*/
static void moduleVec4Check(lua_State *L, int idx, vec4 out) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, idx, "vec4_mt"); JS_FUNC(moduleVec4GetX) {
glm_vec4_copy(*v, out); float_t *v = (float_t *)jerry_object_get_native_ptr(
} call_info_p->this_value, &VEC4_NATIVE_INFO
/**
* __index metamethod for vec4 userdata.
* Supports integer indices 1-4 and string keys x/u0, y/v0, z/u1, w/v1;
* falls through to the metatable for method lookups.
*
* @param L Lua state. Arg 1: vec4 userdata. Arg 2: integer or string key.
* @return 1.
*/
static int moduleVec4Index(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(lua_isnumber(L, 2)) {
lua_Integer i = lua_tointeger(L, 2);
if(i >= 1 && i <= 4) {
lua_pushnumber(L, (lua_Number)(*v)[i - 1]);
return 1;
}
return 0;
}
if(stringEquals(key, "x") || stringEquals(key, "u0")) {
lua_pushnumber(L, (lua_Number)(*v)[0]);
return 1;
}
if(stringEquals(key, "y") || stringEquals(key, "v0")) {
lua_pushnumber(L, (lua_Number)(*v)[1]);
return 1;
}
if(stringEquals(key, "z") || stringEquals(key, "u1")) {
lua_pushnumber(L, (lua_Number)(*v)[2]);
return 1;
}
if(stringEquals(key, "w") || stringEquals(key, "v1")) {
lua_pushnumber(L, (lua_Number)(*v)[3]);
return 1;
}
lua_getmetatable(L, 1);
lua_getfield(L, -1, key);
lua_remove(L, -2);
return 1;
}
/**
* __newindex metamethod for vec4 userdata.
* Writes x/u0, y/v0, z/u1, w/v1. Errors on unknown keys.
*
* @param L Lua state. Arg 1: vec4 userdata. Arg 2: key string. Arg 3: number.
* @return 0.
*/
static int moduleVec4NewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
const char *key = luaL_checkstring(L, 2);
assertStrLenMin(key, 1, "property key cannot be empty");
if(stringEquals(key, "x") || stringEquals(key, "u0")) {
(*v)[0] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "y") || stringEquals(key, "v0")) {
(*v)[1] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "z") || stringEquals(key, "u1")) {
(*v)[2] = (float_t)luaL_checknumber(L, 3);
return 0;
}
if(stringEquals(key, "w") || stringEquals(key, "v1")) {
(*v)[3] = (float_t)luaL_checknumber(L, 3);
return 0;
}
luaL_error(L, "vec4: unknown property '%s'", key);
return 0;
}
/**
* __add metamethod: returns a + b as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpAdd(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
vec4 r;
glm_vec4_add(*a, *b, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __sub metamethod: returns a - b as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpSub(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
vec4 r;
glm_vec4_sub(*a, *b, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __mul metamethod: returns v * scalar or scalar * v as a new vec4.
*
* @param L Lua state. Arg 1: vec4 or number. Arg 2: number or vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpMul(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 r;
if(lua_isnumber(L, 1)) {
float_t s = (float_t)lua_tonumber(L, 1);
vec4 *v = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
glm_vec4_scale(*v, s, r);
} else {
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
glm_vec4_scale(*v, s, r);
}
moduleVec4Push(L, r);
return 1;
}
/**
* __div metamethod: returns v / scalar as a new vec4.
*
* @param L Lua state. Arg 1: vec4. Arg 2: number.
* @return 1 (new vec4).
*/
static int moduleVec4OpDiv(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
float_t s = (float_t)luaL_checknumber(L, 2);
vec4 r;
glm_vec4_divs(*v, s, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __unm metamethod: returns -v as a new vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4OpUnm(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_negate_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* __eq metamethod: component-wise equality test.
*
* @param L Lua state. Arg 1: vec4. Arg 2: vec4.
* @return 1 (boolean).
*/
static int moduleVec4OpEq(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
lua_pushboolean(L,
(*a)[0] == (*b)[0] && (*a)[1] == (*b)[1] &&
(*a)[2] == (*b)[2] && (*a)[3] == (*b)[3]
); );
return 1; return v ? jerry_number(v[0]) : jerry_undefined();
} }
JS_FUNC(moduleVec4SetX) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[1]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetY) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetZ) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[2]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetZ) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[2] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetW) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[3]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetW) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[3] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
// u0/v0/u1/v1 aliases share the same backing floats
JS_FUNC(moduleVec4GetU0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[0]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetU0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[0] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetV0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[1]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetV0) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[1] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetU1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[2]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetU1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[2] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
JS_FUNC(moduleVec4GetV1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
return v ? jerry_number(v[3]) : jerry_undefined();
}
JS_FUNC(moduleVec4SetV1) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(v && args_count > 0) v[3] = (float_t)jerry_value_as_number(args_p[0]);
return jerry_undefined();
}
// ---------------------------------------------------------------------------
// Methods
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec4Dot) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.dot: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.dot: argument must be a vec4");
return jerry_number(glm_vec4_dot(a, b));
}
JS_FUNC(moduleVec4Length) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.length: invalid this");
return jerry_number(glm_vec4_norm(v));
}
JS_FUNC(moduleVec4LengthSq) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.lengthSq: invalid this");
return jerry_number(glm_vec4_norm2(v));
}
JS_FUNC(moduleVec4Normalize) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.normalize: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_normalize_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Negate) {
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.negate: invalid this");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_negate_to(v, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Add) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.add: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.add: argument must be a vec4");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_add(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Sub) {
JS_REQUIRE_ARGS(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.sub: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.sub: argument must be a vec4");
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_sub(a, b, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Scale) {
JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
float_t *v = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!v) return JS_THROW("vec4.scale: invalid this");
float_t s = (float_t)jerry_value_as_number(args_p[0]);
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_scale(v, s, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
JS_FUNC(moduleVec4Lerp) {
JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(1);
float_t *a = (float_t *)jerry_object_get_native_ptr(
call_info_p->this_value, &VEC4_NATIVE_INFO
);
if(!a) return JS_THROW("vec4.lerp: invalid this");
float_t *b = (float_t *)jerry_object_get_native_ptr(args_p[0], &VEC4_NATIVE_INFO);
if(!b) return JS_THROW("vec4.lerp: first argument must be a vec4");
float_t t = (float_t)jerry_value_as_number(args_p[1]);
float_t *r = (float_t *)malloc(sizeof(vec4));
glm_vec4_lerp(a, b, t, r);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, r);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Constructor
// ---------------------------------------------------------------------------
JS_FUNC(moduleVec4Create) {
JS_REQUIRE_ARGS(4);
JS_REQUIRE_NUMBER(0);
JS_REQUIRE_NUMBER(1);
JS_REQUIRE_NUMBER(2);
JS_REQUIRE_NUMBER(3);
float_t *v = (float_t *)malloc(sizeof(vec4));
v[0] = (float_t)jerry_value_as_number(args_p[0]);
v[1] = (float_t)jerry_value_as_number(args_p[1]);
v[2] = (float_t)jerry_value_as_number(args_p[2]);
v[3] = (float_t)jerry_value_as_number(args_p[3]);
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, v);
jerry_object_set_proto(obj, s_vec4Proto);
return obj;
}
// ---------------------------------------------------------------------------
// Helper: push a cglm vec4 as a new JS object
// ---------------------------------------------------------------------------
/** /**
* __tostring metamethod: returns a human-readable representation. * Wraps a copy of a cglm vec4 as a new JerryScript object.
* *
* @param L Lua state. Arg 1: vec4. * @param v Source float[4] to copy.
* @return 1 (string). * @return Owned jerry_value_t with native ptr set.
*/ */
static int moduleVec4OpToString(lua_State *L) { static inline jerry_value_t moduleVec4Push(const float_t *v) {
assertNotNull(L, "Lua state cannot be NULL"); float_t *copy = (float_t *)malloc(sizeof(vec4));
copy[0] = v[0];
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); copy[1] = v[1];
assertNotNull(v, "invalid vec4 userdata"); copy[2] = v[2];
copy[3] = v[3];
char buf[96]; jerry_value_t obj = jerry_object();
snprintf(buf, sizeof(buf), "vec4(%.3f, %.3f, %.3f, %.3f)", (*v)[0], (*v)[1], (*v)[2], (*v)[3]); jerry_object_set_native_ptr(obj, &VEC4_NATIVE_INFO, copy);
lua_pushstring(L, buf); jerry_object_set_proto(obj, s_vec4Proto);
return 1; return obj;
} }
// ---------------------------------------------------------------------------
// Helper: extract vec4 from a JS object
// ---------------------------------------------------------------------------
/** /**
* Returns the dot product of two vec4 values. * Reads a JerryScript vec4 object into an existing float[4].
* *
* @param L Lua state. Arg 1: vec4. Arg 2: vec4. * @param val JS value to read from.
* @return 1 (number). * @param out Destination float[4].
* @return true if val carries a valid vec4 native ptr.
*/ */
static int moduleVec4Dot(lua_State *L) { static inline bool_t moduleVec4Check(jerry_value_t val, float_t *out) {
assertNotNull(L, "Lua state cannot be NULL"); float_t *v = (float_t *)jerry_object_get_native_ptr(val, &VEC4_NATIVE_INFO);
if(!v) return false;
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); out[0] = v[0];
assertNotNull(a, "invalid vec4 userdata"); out[1] = v[1];
out[2] = v[2];
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt"); out[3] = v[3];
assertNotNull(b, "invalid vec4 userdata"); return true;
lua_pushnumber(L, (lua_Number)glm_vec4_dot(*a, *b));
return 1;
} }
// ---------------------------------------------------------------------------
// Module init
// ---------------------------------------------------------------------------
/** /**
* Returns the length (magnitude) of a vec4. * Creates the vec4 prototype with x/y/z/w (and u0/v0/u1/v1 alias)
* * getter-setter properties and all methods, then registers the global
* @param L Lua state. Arg 1: vec4. * vec4() constructor.
* @return 1 (number).
*/ */
static int moduleVec4Length(lua_State *L) { static void moduleVec4(void) {
assertNotNull(L, "Lua state cannot be NULL"); s_vec4Proto = jerry_object();
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); // Primary component names
assertNotNull(v, "invalid vec4 userdata"); jsDefineProperty(s_vec4Proto, "x", moduleVec4GetX, moduleVec4SetX);
jsDefineProperty(s_vec4Proto, "y", moduleVec4GetY, moduleVec4SetY);
jsDefineProperty(s_vec4Proto, "z", moduleVec4GetZ, moduleVec4SetZ);
jsDefineProperty(s_vec4Proto, "w", moduleVec4GetW, moduleVec4SetW);
lua_pushnumber(L, (lua_Number)glm_vec4_norm(*v)); // UV alias names (same backing components)
return 1; jsDefineProperty(s_vec4Proto, "u0", moduleVec4GetU0, moduleVec4SetU0);
} jsDefineProperty(s_vec4Proto, "v0", moduleVec4GetV0, moduleVec4SetV0);
jsDefineProperty(s_vec4Proto, "u1", moduleVec4GetU1, moduleVec4SetU1);
/** jsDefineProperty(s_vec4Proto, "v1", moduleVec4GetV1, moduleVec4SetV1);
* Returns the squared length of a vec4.
* jsDefineMethod(s_vec4Proto, "dot", moduleVec4Dot);
* @param L Lua state. Arg 1: vec4. jsDefineMethod(s_vec4Proto, "length", moduleVec4Length);
* @return 1 (number). jsDefineMethod(s_vec4Proto, "lengthSq", moduleVec4LengthSq);
*/ jsDefineMethod(s_vec4Proto, "normalize", moduleVec4Normalize);
static int moduleVec4LengthSq(lua_State *L) { jsDefineMethod(s_vec4Proto, "negate", moduleVec4Negate);
assertNotNull(L, "Lua state cannot be NULL"); jsDefineMethod(s_vec4Proto, "add", moduleVec4Add);
jsDefineMethod(s_vec4Proto, "sub", moduleVec4Sub);
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt"); jsDefineMethod(s_vec4Proto, "scale", moduleVec4Scale);
assertNotNull(v, "invalid vec4 userdata"); jsDefineMethod(s_vec4Proto, "lerp", moduleVec4Lerp);
lua_pushnumber(L, (lua_Number)glm_vec4_norm2(*v)); jsRegister("vec4", moduleVec4Create);
return 1;
}
/**
* Returns a normalized copy of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4Normalize(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_normalize_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Linearly interpolates between two vec4 values.
*
* @param L Lua state. Arg 1: vec4 a. Arg 2: vec4 b. Arg 3: number t.
* @return 1 (new vec4).
*/
static int moduleVec4Lerp(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *a = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(a, "invalid vec4 userdata");
vec4 *b = (vec4 *)luaL_checkudata(L, 2, "vec4_mt");
assertNotNull(b, "invalid vec4 userdata");
float_t t = (float_t)luaL_checknumber(L, 3);
vec4 r;
glm_vec4_lerp(*a, *b, t, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Returns a negated copy of a vec4.
*
* @param L Lua state. Arg 1: vec4.
* @return 1 (new vec4).
*/
static int moduleVec4Negate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 *v = (vec4 *)luaL_checkudata(L, 1, "vec4_mt");
assertNotNull(v, "invalid vec4 userdata");
vec4 r;
glm_vec4_negate_to(*v, r);
moduleVec4Push(L, r);
return 1;
}
/**
* Constructor: creates a vec4 from four optional numbers (defaults to 0).
*
* @param L Lua state. Args 1-4 (optional): x, y, z, w.
* @return 1 (new vec4).
*/
static int moduleVec4Create(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
vec4 v = {0, 0, 0, 0};
int top = lua_gettop(L);
if(top >= 1) v[0] = (float_t)luaL_checknumber(L, 1);
if(top >= 2) v[1] = (float_t)luaL_checknumber(L, 2);
if(top >= 3) v[2] = (float_t)luaL_checknumber(L, 3);
if(top >= 4) v[3] = (float_t)luaL_checknumber(L, 4);
moduleVec4Push(L, v);
return 1;
}
/**
* Registers the vec4 metatable and vec4 constructor global.
*
* @param L Lua state.
*/
static void moduleVec4(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!luaL_newmetatable(L, "vec4_mt")) {
lua_pop(L, 1);
return;
}
lua_pushcfunction(L, moduleVec4Index);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, moduleVec4NewIndex);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, moduleVec4OpAdd);
lua_setfield(L, -2, "__add");
lua_pushcfunction(L, moduleVec4OpSub);
lua_setfield(L, -2, "__sub");
lua_pushcfunction(L, moduleVec4OpMul);
lua_setfield(L, -2, "__mul");
lua_pushcfunction(L, moduleVec4OpDiv);
lua_setfield(L, -2, "__div");
lua_pushcfunction(L, moduleVec4OpUnm);
lua_setfield(L, -2, "__unm");
lua_pushcfunction(L, moduleVec4OpEq);
lua_setfield(L, -2, "__eq");
lua_pushcfunction(L, moduleVec4OpToString);
lua_setfield(L, -2, "__tostring");
lua_pushcfunction(L, moduleVec4Dot);
lua_setfield(L, -2, "dot");
lua_pushcfunction(L, moduleVec4Length);
lua_setfield(L, -2, "length");
lua_pushcfunction(L, moduleVec4LengthSq);
lua_setfield(L, -2, "lengthSq");
lua_pushcfunction(L, moduleVec4Normalize);
lua_setfield(L, -2, "normalize");
lua_pushcfunction(L, moduleVec4Lerp);
lua_setfield(L, -2, "lerp");
lua_pushcfunction(L, moduleVec4Negate);
lua_setfield(L, -2, "negate");
lua_pop(L, 1);
lua_register(L, "vec4", moduleVec4Create);
} }
+20 -22
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -24,24 +24,22 @@
#include "script/module/display/moduletileset.h" #include "script/module/display/moduletileset.h"
#include "script/module/scene/modulescene.h" #include "script/module/scene/modulescene.h"
void moduleRegister(lua_State *L) { static void moduleRegister(void) {
assertNotNull(L, "Lua state cannot be NULL"); moduleScript();
moduleEntity();
moduleScript(L); moduleInput();
moduleEntity(L); modulePlatform();
moduleInput(L); moduleLocale();
modulePlatform(L); moduleTime();
moduleLocale(L); moduleEvent();
moduleTime(L); moduleColor();
moduleEvent(L); moduleSpriteBatch();
moduleColor(L); moduleMath();
moduleSpriteBatch(L); moduleShader();
moduleMath(L); moduleUi();
moduleShader(L); moduleText();
moduleUi(L); moduleScreen();
moduleText(L); moduleTexture();
moduleScreen(L); moduleTileset();
moduleTexture(L); moduleScene();
moduleTileset(L); }
moduleScene(L);
}
+255 -2
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -9,4 +9,257 @@
#include "script/scriptcontext.h" #include "script/scriptcontext.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "util/string.h" #include "util/string.h"
#include "util/memory.h" #include "util/memory.h"
#include <stdlib.h>
/**
* Standard JerryScript external function signature.
* Usage: JS_FUNC(myFunction) { ... }
*/
#define JS_FUNC(name) \
static jerry_value_t name( \
const jerry_call_info_t *call_info_p, \
const jerry_value_t args_p[], \
const jerry_length_t args_count)
/**
* Return a type-error exception from a module function.
* Usage: return JS_THROW("message");
*/
#define JS_THROW(msg) jerry_throw_sz(JERRY_ERROR_TYPE, (msg))
/**
* Assert minimum argument count; return type error if not met.
*/
#define JS_REQUIRE_ARGS(n) do { \
if((jerry_length_t)(args_count) < (jerry_length_t)(n)) { \
return JS_THROW("Not enough arguments"); \
} \
} while(0)
/**
* Assert an argument is a number; return type error if not.
*/
#define JS_REQUIRE_NUMBER(i) do { \
if(!jerry_value_is_number(args_p[(i)])) { \
return JS_THROW("Expected number argument"); \
} \
} while(0)
/**
* Assert an argument is a string; return type error if not.
*/
#define JS_REQUIRE_STRING(i) do { \
if(!jerry_value_is_string(args_p[(i)])) { \
return JS_THROW("Expected string argument"); \
} \
} while(0)
/**
* Assert an argument is a function; return type error if not.
*/
#define JS_REQUIRE_FUNCTION(i) do { \
if(!jerry_value_is_function(args_p[(i)])) { \
return JS_THROW("Expected function argument"); \
} \
} while(0)
/**
* Assert an argument is an object; return type error if not.
*/
#define JS_REQUIRE_OBJECT(i) do { \
if(!jerry_value_is_object(args_p[(i)])) { \
return JS_THROW("Expected object argument"); \
} \
} while(0)
/* JS_PTR_NATIVE_INFO is declared in scriptcontext.h and defined in
scriptcontext.c so all TUs share a single address for native-ptr lookups. */
/**
* Register a global JS function.
*
* @param name Function name as seen in scripts.
* @param fn C handler function.
*/
static inline void jsRegister(
const char_t *name,
jerry_external_handler_t fn
) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_value_t func = jerry_function_external(fn);
jerry_object_set(global, key, func);
jerry_value_free(func);
jerry_value_free(key);
jerry_value_free(global);
}
/**
* Set a global numeric constant.
*/
static inline void jsSetNumber(const char_t *name, double value) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_value_t val = jerry_number(value);
jerry_object_set(global, key, val);
jerry_value_free(val);
jerry_value_free(key);
jerry_value_free(global);
}
/**
* Set a global integer constant.
*/
static inline void jsSetInt(const char_t *name, int32_t value) {
jsSetNumber(name, (double)value);
}
/**
* Set a global string constant.
*/
static inline void jsSetString(const char_t *name, const char_t *value) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_value_t val = jerry_string_sz(value);
jerry_object_set(global, key, val);
jerry_value_free(val);
jerry_value_free(key);
jerry_value_free(global);
}
/**
* Set a global JS value. Caller retains ownership of the value and must free
* it independently.
*/
static inline void jsSetValue(const char_t *name, jerry_value_t value) {
jerry_value_t global = jerry_current_realm();
jerry_value_t key = jerry_string_sz(name);
jerry_object_set(global, key, value);
jerry_value_free(key);
jerry_value_free(global);
}
/**
* Wrap an engine-owned C pointer as a JS object (no GC free callback).
* Used for global singletons like INPUT_EVENT_PRESSED, SHADER_UNLIT, etc.
*/
static inline jerry_value_t jsWrapPointer(void *ptr) {
jerry_value_t obj = jerry_object();
jerry_object_set_native_ptr(obj, &JS_PTR_NATIVE_INFO, ptr);
return obj;
}
/**
* Unwrap a C pointer from a JS object created by jsWrapPointer.
* Returns NULL if the object does not carry a matching native pointer.
*/
static inline void *jsUnwrapPointer(jerry_value_t val) {
if(!jerry_value_is_object(val)) return NULL;
return jerry_object_get_native_ptr(val, &JS_PTR_NATIVE_INFO);
}
/**
* Evaluate a JS source string in the global scope.
* Errors are silently discarded; use scriptContextExec for error-propagating
* execution.
*/
static inline void jsEvalStr(const char_t *script) {
jerry_value_t result = jerry_eval(
(const jerry_char_t *)script,
strlen(script),
JERRY_PARSE_NO_OPTS
);
jerry_value_free(result);
}
/**
* Copy a JerryScript string value into a C buffer (null-terminated).
*
* @param val Jerry string value.
* @param buf Output buffer.
* @param buflen Buffer capacity including the null terminator.
*/
static inline void jsToString(
jerry_value_t val,
char_t *buf,
jerry_size_t buflen
) {
jerry_size_t len = jerry_string_to_buffer(
val, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, buflen - 1
);
buf[len] = '\0';
}
/**
* Define a named property on a JS object with getter and optional setter.
* Both getter and setter are external C functions.
*
* @param obj Target object (e.g. a prototype).
* @param name Property name.
* @param getter C getter handler.
* @param setter C setter handler, or NULL for read-only property.
*/
static inline void jsDefineProperty(
jerry_value_t obj,
const char_t *name,
jerry_external_handler_t getter,
jerry_external_handler_t setter
) {
jerry_property_descriptor_t desc;
memset(&desc, 0, sizeof(desc));
desc.flags = (uint16_t)(
JERRY_PROP_IS_GET_DEFINED |
JERRY_PROP_IS_ENUMERABLE_DEFINED | JERRY_PROP_IS_ENUMERABLE |
JERRY_PROP_IS_CONFIGURABLE_DEFINED | JERRY_PROP_IS_CONFIGURABLE
);
desc.getter = jerry_function_external(getter);
if(setter != NULL) {
desc.flags |= JERRY_PROP_IS_SET_DEFINED;
desc.setter = jerry_function_external(setter);
}
jerry_value_t key = jerry_string_sz(name);
jerry_value_t result = jerry_object_define_own_prop(obj, key, &desc);
jerry_value_free(result);
jerry_value_free(key);
jerry_value_free(desc.getter);
if(setter != NULL) jerry_value_free(desc.setter);
}
/**
* Set a named method (C function) on a JS object.
*
* @param obj Target object (e.g. a prototype).
* @param name Method name.
* @param fn C handler function.
*/
static inline void jsDefineMethod(
jerry_value_t obj,
const char_t *name,
jerry_external_handler_t fn
) {
jerry_value_t key = jerry_string_sz(name);
jerry_value_t func = jerry_function_external(fn);
jerry_object_set(obj, key, func);
jerry_value_free(func);
jerry_value_free(key);
}
/**
* Format an error message from a JerryScript exception value.
* Caller must ensure buf is large enough.
*/
static inline void jsExceptionMessage(
jerry_value_t exception,
char_t *buf,
size_t buflen
) {
jerry_value_t errVal = jerry_exception_value(exception, false);
jerry_value_t errStr = jerry_value_to_string(errVal);
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, (jerry_size_t)(buflen - 1)
);
buf[len] = '\0';
jerry_value_free(errStr);
jerry_value_free(errVal);
}
+6 -9
View File
@@ -1,27 +1,24 @@
/** /**
* Copyright (c) 2025 Dominic Masters * Copyright (c) 2025 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/scriptcontext.h" #include "script/module/modulebase.h"
#include "assert/assert.h"
#include "script/module/moduleplatformplatform.h" #include "script/module/moduleplatformplatform.h"
#ifndef DUSK_TARGET_SYSTEM #ifndef DUSK_TARGET_SYSTEM
#error "DUSK_TARGET_SYSTEM must be defined" #error "DUSK_TARGET_SYSTEM must be defined"
#endif #endif
#define MODULE_PLATFORM_VALUE "PLATFORM = '" DUSK_TARGET_SYSTEM "'\n" #define MODULE_PLATFORM_VALUE "var PLATFORM = '" DUSK_TARGET_SYSTEM "';\n"
static void modulePlatform(lua_State *L) { static void modulePlatform(void) {
assertNotNull(L, "Lua state cannot be NULL"); jsEvalStr(MODULE_PLATFORM_VALUE);
luaL_dostring(L, MODULE_PLATFORM_VALUE);
#ifdef modulePlatformPlatform #ifdef modulePlatformPlatform
modulePlatformPlatform(L); modulePlatformPlatform();
#endif #endif
} }
+45 -95
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -9,118 +9,68 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "scene/scene.h" #include "scene/scene.h"
/** JS_FUNC(moduleSceneSet) {
* __index metamethod for the Scene table. Handles dynamic read-only properties. JS_REQUIRE_ARGS(1);
* JS_REQUIRE_STRING(0);
* @param L Lua state. Arg 1: table, Arg 2: key.
* @return 1.
*/
static int moduleSceneIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
const char_t *key = lua_tostring(L, 2); char_t name[ASSET_FILE_PATH_MAX];
assertNotNull(key, "Scene property key cannot be NULL"); jsToString(args_p[0], name, sizeof(name));
if(name[0] == '\0') return JS_THROW("Scene.set: Scene name cannot be empty");
// if(stringEquals(key, "current")) { sceneSet(name);
// if(SCENE.sceneActive) { return jerry_undefined();
// lua_pushstring(L, SCENE.sceneCurrent);
// } else {
// lua_pushnil(L);
// }
// return 1;
// }
lua_pushnil(L);
return 1;
} }
/** JS_FUNC(moduleSceneGetCurrent) {
* Attached Scene.set method to invoke internal C method. if(SCENE.sceneCurrent[0] == '\0') return jerry_undefined();
* return jerry_string_sz(SCENE.sceneCurrent);
* @param L Lua state. }
* @return Number of return values on the Lua stack.
*/
static int moduleSceneSet(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) { static void moduleSceneReset(void) {
luaL_error(L, "Scene.set requires a string argument"); if(SCENE.scriptRef != SCENE_SCRIPT_REF_NONE) {
return 0; jerry_value_free(SCENE.scriptRef);
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
} }
sceneSet(lua_tostring(L, 1)); jerry_value_t obj = jerry_object();
return 0;
jsDefineMethod(obj, "set", moduleSceneSet);
jsDefineProperty(obj, "current", moduleSceneGetCurrent, NULL);
jsSetValue("Scene", obj);
jerry_value_free(obj);
} }
/** static errorret_t moduleSceneCall(const char_t *method) {
* Resets the scene back to a clean slate, this is called before loading a new
* scene to ensure that no old state bleeds through.
*/
static void moduleSceneReset(lua_State *L) {
if(SCENE.scriptRef != LUA_NOREF) {
luaL_unref(L, LUA_REGISTRYINDEX, SCENE.scriptRef);
SCENE.scriptRef = LUA_NOREF;
}
lua_newtable(L);
// Scene.set
lua_pushcfunction(L, moduleSceneSet);
lua_setfield(L, -2, "set");
// Metatable for dynamic read-only properties (e.g. Scene.current)
lua_newtable(L);
lua_pushcfunction(L, moduleSceneIndex);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_setglobal(L, "Scene");
}
/**
* Invokes a method on the current scene.
*
* @param method Which method to call.
* @return Any error state that happened.
*/
static errorret_t moduleSceneCall(lua_State *L, const char_t *method) {
assertNotNull(L, "Lua state cannot be NULL");
assertStrLenMin(method, 1, "Method name cannot be empty"); assertStrLenMin(method, 1, "Method name cannot be empty");
assertTrue(
SCENE.scriptRef != LUA_NOREF && SCENE.scriptRef != LUA_REFNIL,
"No active scene script to call method on"
);
// Get the scene table if(SCENE.scriptRef == SCENE_SCRIPT_REF_NONE) {
lua_rawgeti(L, LUA_REGISTRYINDEX, SCENE.scriptRef); errorThrow("No active scene script to call method on");
if(!lua_istable(L, -1)) {
lua_pop(L, 1);
errorThrow("Scene script ref %d is not a table", SCENE.scriptRef);
} }
// Get the method from the scene table jerry_value_t key = jerry_string_sz(method);
lua_getfield(L, -1, method); jerry_value_t fn = jerry_object_get(SCENE.scriptRef, key);
if(!lua_isfunction(L, -1)) { jerry_value_free(key);
lua_pop(L, 2);
errorThrow("Scene method '%s' not found", method);// TODO: Needed? if(!jerry_value_is_function(fn)) {
jerry_value_free(fn);
errorThrow("Scene method '%s' not found", method);
} }
// Push the scene table as the first argument (self) jerry_value_t result = jerry_call(fn, SCENE.scriptRef, NULL, 0);
lua_pushvalue(L, -2); jerry_value_free(fn);
// Call the method with 1 argument (the scene table) and 0 return values if(jerry_value_is_exception(result)) {
if(lua_pcall(L, 1, 0, 0) != LUA_OK) { char_t errMsg[512];
const char_t *err = lua_tostring(L, -1); jsExceptionMessage(result, errMsg, sizeof(errMsg));
lua_pop(L, 2);// Pops the error message and the scene table jerry_value_free(result);
errorThrow("Scene:%s failed: %s", method, err); errorThrow("Scene:%s failed: %s", method, errMsg);
} }
lua_pop(L, 1);// Pops the scene table jerry_value_free(result);
errorOk(); errorOk();
} }
static void moduleScene(lua_State *L) { static void moduleScene(void) {
assertNotNull(L, "Lua state cannot be NULL"); moduleSceneReset();
}
moduleSceneReset(L);
}
+40 -46
View File
@@ -9,74 +9,68 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "console/console.h" #include "console/console.h"
static int moduleScriptPrint(lua_State *L) { JS_FUNC(moduleScriptPrint) {
assertNotNull(L, "Lua state cannot be NULL"); char_t buf[512];
char_t msg[4096];
size_t msgLen = 0;
int n = lua_gettop(L); for(jerry_length_t i = 0; i < args_count; ++i) {
luaL_Buffer b; jerry_value_t strVal = jerry_value_to_string(args_p[i]);
luaL_buffinit(L, &b); jsToString(strVal, buf, sizeof(buf));
jerry_value_free(strVal);
for(int i = 1; i <= n; ++i) { size_t partLen = strlen(buf);
size_t len; if(msgLen + partLen + 1 < sizeof(msg)) {
const char *s = luaL_tolstring(L, i, &len); stringCopy(msg + msgLen, buf, sizeof(msg) - msgLen);
luaL_addlstring(&b, s, len); msgLen += partLen;
lua_pop(L, 1); }
if(i < n) luaL_addlstring(&b, "\t", 1);
if(i + 1 < args_count && msgLen + 1 < sizeof(msg)) {
msg[msgLen++] = '\t';
msg[msgLen] = '\0';
}
} }
luaL_pushresult(&b);
const char *msg = lua_tostring(L, -1);
consolePrint("%s", msg); consolePrint("%s", msg);
return 0; return jerry_undefined();
} }
static int moduleScriptInclude(lua_State *L) { JS_FUNC(moduleScriptInclude) {
assertNotNull(L, "Lua state cannot be NULL"); if(args_count < 1 || !jerry_value_is_string(args_p[0])) {
return JS_THROW("Expected string filename");
if(!lua_isstring(L, 1)) {
luaL_error(L, "Expected string filename");
return 0;
} }
scriptcontext_t* ctx = *(scriptcontext_t**)lua_getextraspace(L); char_t filename[1024];
if(ctx == NULL) { jsToString(args_p[0], filename, sizeof(filename));
luaL_error(L, "Script context is NULL");
return 0;
}
const char_t *filename = luaL_checkstring(L, 1); if(filename[0] == '\0') {
if(filename == NULL || filename[0] == '\0') { return JS_THROW("Filename cannot be empty");
luaL_error(L, "Filename cannot be NULL");
return 0;
} }
char_t buffer[1024]; char_t buffer[1024];
stringCopy(buffer, filename, 1024); stringCopy(buffer, filename, sizeof(buffer));
size_t len = strlen(buffer); size_t len = strlen(buffer);
if(len < 4 || stringCompare(&buffer[len - 4], ".lua") != 0) { if(len < 3 || stringCompare(&buffer[len - 3], ".js") != 0) {
if(len + 4 >= 1024) { if(len + 3 >= sizeof(buffer)) {
luaL_error(L, "Filename too long to append .lua"); return JS_THROW("Filename too long to append .js");
return 0;
} }
stringCopy(&buffer[len], ".lua", 5); stringCopy(&buffer[len], ".js", 4);
} }
int32_t stackBase = lua_gettop(L); jerry_value_t result = 0;
errorret_t err = scriptContextExecFile(scriptContextCurrent, buffer, &result);
errorret_t err = scriptContextExecFile(ctx, buffer);
if(err.code != ERROR_OK) { if(err.code != ERROR_OK) {
luaL_error(L, "Failed to include script file: %s", buffer); if(result != 0) jerry_value_free(result);
errorCatch(errorPrint(err)); errorCatch(errorPrint(err));
return 0; return JS_THROW("Failed to include script file");
} }
return lua_gettop(L) - stackBase; if(result == 0) return jerry_undefined();
return result;
} }
static void moduleScript(lua_State *L) { static void moduleScript(void) {
assertNotNull(L, "Lua state cannot be NULL"); jsRegister("print", moduleScriptPrint);
jsRegister("include", moduleScriptInclude);
lua_register(L, "print", moduleScriptPrint);
lua_register(L, "include", moduleScriptInclude);
} }
+12 -27
View File
@@ -9,33 +9,18 @@
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
#include "time/time.h" #include "time/time.h"
static int moduleTimeIndex(lua_State *L) { JS_FUNC(moduleTimeGetDelta) {
const char_t *key = lua_tostring(L, 2); return jerry_number(TIME.delta);
assertStrLenMin(key, 1, "Key cannot be empty.");
if(stringCompare(key, "delta") == 0) {
lua_pushnumber(L, TIME.delta);
return 1;
} else if(stringCompare(key, "time") == 0) {
lua_pushnumber(L, TIME.time);
return 1;
}
lua_pushnil(L);
return 1;
} }
static void moduleTime(lua_State *L) { JS_FUNC(moduleTimeGetTime) {
assertNotNull(L, "Lua state cannot be NULL"); return jerry_number(TIME.time);
}
if(luaL_newmetatable(L, "time_mt")) {
lua_pushcfunction(L, moduleTimeIndex); static void moduleTime(void) {
lua_setfield(L, -2, "__index"); jerry_value_t obj = jerry_object();
} jsDefineProperty(obj, "delta", moduleTimeGetDelta, NULL);
lua_pop(L, 1); jsDefineProperty(obj, "time", moduleTimeGetTime, NULL);
jsSetValue("TIME", obj);
dusktime_t **ud = (dusktime_t**)lua_newuserdata(L, sizeof(dusktime_t*)); jerry_value_free(obj);
*ud = &TIME;
luaL_setmetatable(L, "time_mt");
lua_setglobal(L, "TIME");
} }
+1 -2
View File
@@ -8,6 +8,5 @@
#pragma once #pragma once
#include "script/module/modulebase.h" #include "script/module/modulebase.h"
static void moduleUi(lua_State *L) { static void moduleUi(void) {
assertNotNull(L, "Lua state cannot be NULL");
} }
+42 -27
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2025 Dominic Masters * Copyright (c) 2025 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -17,27 +17,26 @@
#include "script/scriptgame.h" #include "script/scriptgame.h"
#endif #endif
scriptcontext_t *scriptContextCurrent = NULL;
const jerry_object_native_info_t JS_PTR_NATIVE_INFO = {
.free_cb = NULL,
.number_of_references = 0,
.offset_of_references = 0
};
errorret_t scriptContextInit(scriptcontext_t *context) { errorret_t scriptContextInit(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
memoryZero(context, sizeof(scriptcontext_t)); memoryZero(context, sizeof(scriptcontext_t));
// Create a new Lua state for this context. jerry_init(JERRY_INIT_EMPTY);
context->luaState = luaL_newstate(); scriptContextCurrent = context;
if(context->luaState == NULL) {
errorThrow("Failed to init Lua state");
}
luaL_openlibs(context->luaState);
// Store context in Lua extraspace moduleRegister();
*(scriptcontext_t**)lua_getextraspace(context->luaState) = context;
// Register built-in script modules.
moduleRegister(context->luaState);
// Fire any game script init function if defined.
#ifdef SCRIPT_GAME_INIT #ifdef SCRIPT_GAME_INIT
SCRIPT_GAME_INIT(L); SCRIPT_GAME_INIT();
#endif #endif
errorOk(); errorOk();
@@ -47,33 +46,49 @@ errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
assertNotNull(script, "Script cannot be NULL"); assertNotNull(script, "Script cannot be NULL");
if(luaL_dostring(context->luaState, script) != LUA_OK) { jerry_value_t result = jerry_eval(
const char_t *strErr = lua_tostring(context->luaState, -1); (const jerry_char_t *)script,
lua_pop(context->luaState, 1); strlen(script),
errorThrow("Failed to execute Lua: %s", strErr); JERRY_PARSE_NO_OPTS
);
if(jerry_value_is_exception(result)) {
jerry_value_t errVal = jerry_exception_value(result, false);
jerry_value_t errStr = jerry_value_to_string(errVal);
char_t buf[256];
jerry_size_t len = jerry_string_to_buffer(
errStr, JERRY_ENCODING_UTF8, (jerry_char_t *)buf, sizeof(buf) - 1
);
buf[len] = '\0';
jerry_value_free(errStr);
jerry_value_free(errVal);
jerry_value_free(result);
errorThrow("Failed to execute script: %s", buf);
} }
jerry_value_free(result);
errorOk(); errorOk();
} }
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname) { errorret_t scriptContextExecFile(
scriptcontext_t *ctx,
const char_t *fname,
jerry_value_t *resultOut
) {
assertNotNull(ctx, "Script context cannot be NULL"); assertNotNull(ctx, "Script context cannot be NULL");
assertNotNull(fname, "Filename cannot be NULL"); assertNotNull(fname, "Filename cannot be NULL");
return assetScriptLoad(fname, ctx); return assetScriptLoad(fname, ctx, resultOut);
} }
void scriptContextDispose(scriptcontext_t *context) { void scriptContextDispose(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL"); assertNotNull(context, "Script context cannot be NULL");
assertNotNull(context->luaState, "Lua state is not initialized");
for(uint8_t i = 0; i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS; i++) { for(uint8_t i = 0; i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS; i++) {
event_t *event = context->subscribedEvents[i]; event_t *event = context->subscribedEvents[i];
if(event == NULL) continue; if(event == NULL) continue;
eventUnsubscribeScriptContext(event, context); eventUnsubscribeScriptContext(event, context);
} }
if(context->luaState != NULL) { jerry_cleanup();
lua_close(context->luaState); scriptContextCurrent = NULL;
context->luaState = NULL; }
}
}
+29 -15
View File
@@ -1,6 +1,6 @@
/** /**
* Copyright (c) 2025 Dominic Masters * Copyright (c) 2025 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
@@ -8,48 +8,62 @@
#pragma once #pragma once
#include "error/error.h" #include "error/error.h"
#include "scriptvalue.h" #include "scriptvalue.h"
#include <lua.h> #include <jerryscript.h>
#include <lauxlib.h>
#include <lualib.h>
typedef struct event_s event_t; typedef struct event_s event_t;
#define SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS 64 #define SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS 64
typedef struct scriptcontext_s { typedef struct scriptcontext_s {
lua_State *luaState;
event_t* subscribedEvents[SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS]; event_t* subscribedEvents[SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS];
} scriptcontext_t; } scriptcontext_t;
/** Points to the currently active script context. Set by scriptContextInit. */
extern scriptcontext_t *scriptContextCurrent;
/**
* Singleton native-info tag for engine-owned C pointers wrapped in JS objects.
* A single global instance ensures jerry_object_get_native_ptr() matches across
* all compilation units (including event.c and module headers).
*/
extern const jerry_object_native_info_t JS_PTR_NATIVE_INFO;
/** /**
* Initialize a script context. * Initialize a script context.
* *
* @param context The script context to initialize. * @param context The script context to initialize.
* @return The error return value. * @return The error return value.
*/ */
errorret_t scriptContextInit(scriptcontext_t *context); errorret_t scriptContextInit(scriptcontext_t *context);
/** /**
* Execute a script within a script context. * Execute a script string within a script context.
* *
* @param context The script context to use. * @param context The script context to use.
* @param script The script to execute. * @param script The JS source to execute.
* @return The error return value. * @return The error return value.
*/ */
errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script); errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script);
/** /**
* Execute a script from a file within a script context. * Execute a script from a file within a script context.
* *
* @param ctx The script context to use. * @param ctx The script context to use.
* @param fname The filename of the script to execute. * @param fname The filename of the script to execute.
* @param result Optional out-parameter for the script's return value.
* Caller must call jerry_value_free() on it when done.
* Pass NULL to discard the return value.
* @return The error return value. * @return The error return value.
*/ */
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname); errorret_t scriptContextExecFile(
scriptcontext_t *ctx,
const char_t *fname,
jerry_value_t *result
);
/** /**
* Dispose of a script context. * Dispose of a script context.
* *
* @param context The script context to dispose of. * @param context The script context to dispose of.
*/ */
void scriptContextDispose(scriptcontext_t *context); void scriptContextDispose(scriptcontext_t *context);
@@ -1,13 +1,13 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/scriptcontext.h" #include "script/module/modulebase.h"
static void modulePlatformDolphin(lua_State *L) { static void modulePlatformDolphin(void) {
luaL_dostring(L, "DOLPHIN = true\n"); jsEvalStr("var DOLPHIN = true;\n");
} }
@@ -1,13 +1,13 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/scriptcontext.h" #include "script/module/modulebase.h"
static void modulePlatformLinux(lua_State *L) { static void modulePlatformLinux(void) {
luaL_dostring(L, "LINUX = true\n"); jsEvalStr("var LINUX = true;\n");
} }
@@ -1,13 +1,13 @@
/** /**
* Copyright (c) 2026 Dominic Masters * Copyright (c) 2026 Dominic Masters
* *
* This software is released under the MIT License. * This software is released under the MIT License.
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#pragma once #pragma once
#include "script/scriptcontext.h" #include "script/module/modulebase.h"
void modulePlatformPSP(lua_State *L) { static void modulePlatformPSP(void) {
luaL_dostring(L, "PSP = true\n"); jsEvalStr("var PSP = true;\n");
} }
+92 -133
View File
@@ -6,236 +6,195 @@
*/ */
#pragma once #pragma once
#include "script/scriptcontext.h" #include "script/module/modulebase.h"
#include "item/inventory.h" #include "item/inventory.h"
#include "item/backpack.h" #include "item/backpack.h"
#include "assert/assert.h" #include "assert/assert.h"
static int moduleInventoryItemExists(lua_State *L) { JS_FUNC(moduleInventoryItemExists) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryItemExists: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryItemExists: Expected inventory pointer as first argument");
return 0;
} }
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "inventoryItemExists: Expected item ID as second argument"); return JS_THROW("inventoryItemExists: Expected item ID as second argument");
return 0;
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)lua_tonumber(L, 2); itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
if(item == ITEM_ID_NULL) { if(item == ITEM_ID_NULL) {
luaL_error(L, "inventoryItemExists: Item ID cannot be ITEM_ID_NULL"); return JS_THROW("inventoryItemExists: Item ID cannot be ITEM_ID_NULL");
return 0;
} }
bool_t hasItem = inventoryItemExists(inventory, item); bool_t hasItem = inventoryItemExists(inventory, item);
lua_pushboolean(L, hasItem); return jerry_boolean(hasItem);
return 1;
} }
static int moduleInventorySet(lua_State *L) { JS_FUNC(moduleInventorySet) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventorySet: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventorySet: Expected inventory pointer as first argument");
return 0;
} }
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "inventorySet: Expected item ID as second argument"); return JS_THROW("inventorySet: Expected item ID as second argument");
return 0;
} }
if(!lua_isnumber(L, 3)) { if(!jerry_value_is_number(args_p[2])) {
luaL_error(L, "inventorySet: Expected quantity as third argument"); return JS_THROW("inventorySet: Expected quantity as third argument");
return 0;
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)lua_tonumber(L, 2); itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
uint8_t quantity = (uint8_t)lua_tonumber(L, 3); uint8_t quantity = (uint8_t)jerry_value_as_number(args_p[2]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
inventorySet(inventory, item, quantity); inventorySet(inventory, item, quantity);
return 0; return jerry_undefined();
} }
static int moduleInventoryAdd(lua_State *L) { JS_FUNC(moduleInventoryAdd) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryAdd: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryAdd: Expected inventory pointer as first argument");
return 0;
} }
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "inventoryAdd: Expected item ID as second argument"); return JS_THROW("inventoryAdd: Expected item ID as second argument");
return 0;
} }
if(!lua_isnumber(L, 3)) { if(!jerry_value_is_number(args_p[2])) {
luaL_error(L, "inventoryAdd: Expected quantity as third argument"); return JS_THROW("inventoryAdd: Expected quantity as third argument");
return 0;
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)lua_tonumber(L, 2); itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
uint8_t quantity = (uint8_t)lua_tonumber(L, 3); uint8_t quantity = (uint8_t)jerry_value_as_number(args_p[2]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
inventoryAdd(inventory, item, quantity); inventoryAdd(inventory, item, quantity);
return 0; return jerry_undefined();
} }
static int moduleInventoryRemove(lua_State *L) { JS_FUNC(moduleInventoryRemove) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryRemove: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryRemove: Expected inventory pointer as first argument");
return 0;
} }
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "inventoryRemove: Expected item ID as second argument"); return JS_THROW("inventoryRemove: Expected item ID as second argument");
return 0;
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)lua_tonumber(L, 2); itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
if(lua_gettop(L) >= 3) { if(args_count >= 3) {
if(!lua_isnumber(L, 3)) { if(!jerry_value_is_number(args_p[2])) {
luaL_error(L, "inventoryRemove: Expected quantity as third argument"); return JS_THROW("inventoryRemove: Expected quantity as third argument");
return 0;
} }
uint8_t amount = (uint8_t)lua_tonumber(L, 3); uint8_t amount = (uint8_t)jerry_value_as_number(args_p[2]);
uint8_t currentQuantity = inventoryGetCount(inventory, item); uint8_t currentQuantity = inventoryGetCount(inventory, item);
if(amount >= currentQuantity) { if(amount >= currentQuantity) {
inventoryRemove(inventory, item); inventoryRemove(inventory, item);
return 0; return jerry_undefined();
} }
inventorySet(inventory, item, currentQuantity - amount); inventorySet(inventory, item, currentQuantity - amount);
return 0; return jerry_undefined();
} }
inventoryRemove(inventory, item); inventoryRemove(inventory, item);
return 0; return jerry_undefined();
} }
static int moduleInventoryGetCount(lua_State *L) { JS_FUNC(moduleInventoryGetCount) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryGetCount: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryGetCount: Expected inventory pointer as first argument");
return 0;
} }
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "inventoryGetCount: Expected item ID as second argument"); return JS_THROW("inventoryGetCount: Expected item ID as second argument");
return 0;
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)lua_tonumber(L, 2); itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
uint8_t count = inventoryGetCount(inventory, item); uint8_t count = inventoryGetCount(inventory, item);
lua_pushnumber(L, count); return jerry_number(count);
return 1;
} }
static int moduleInventoryIsFull(lua_State *L) { JS_FUNC(moduleInventoryIsFull) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryIsFull: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryIsFull: Expected inventory pointer as first argument");
return 0;
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
bool_t isFull = inventoryIsFull(inventory); bool_t isFull = inventoryIsFull(inventory);
lua_pushboolean(L, isFull); return jerry_boolean(isFull);
return 1;
} }
static int moduleInventoryItemFull(lua_State *L) { JS_FUNC(moduleInventoryItemFull) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventoryItemFull: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventoryItemFull: Expected inventory pointer as first argument");
return 0;
} }
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "inventoryItemFull: Expected item ID as second argument"); return JS_THROW("inventoryItemFull: Expected item ID as second argument");
return 0;
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
itemid_t item = (itemid_t)lua_tonumber(L, 2); itemid_t item = (itemid_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
bool_t isFull = inventoryItemFull(inventory, item); bool_t isFull = inventoryItemFull(inventory, item);
lua_pushboolean(L, isFull); return jerry_boolean(isFull);
return 1;
} }
static int moduleInventorySort(lua_State *L) { JS_FUNC(moduleInventorySort) {
assertNotNull(L, "Lua state cannot be NULL"); if(!jerry_value_is_object(args_p[0])) {
return JS_THROW("inventorySort: Expected inventory pointer as first argument");
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "inventorySort: Expected inventory pointer as first argument");
return 0;
} }
if(!lua_isnumber(L, 2)) { if(!jerry_value_is_number(args_p[1])) {
luaL_error(L, "inventorySort: Expected sort type as second argument"); return JS_THROW("inventorySort: Expected sort type as second argument");
return 0;
} }
bool_t reverse = false; bool_t reverse = false;
if(lua_gettop(L) >= 3) { if(args_count >= 3) {
if(!lua_isboolean(L, 3)) { if(!jerry_value_is_boolean(args_p[2])) {
luaL_error(L, "inventorySort: Expected reverse flag as third argument"); return JS_THROW("inventorySort: Expected reverse flag as third argument");
return 0;
} }
reverse = (bool_t)lua_toboolean(L, 3); reverse = (bool_t)jerry_value_is_true(args_p[2]);
} }
inventory_t *inventory = (inventory_t *)lua_touserdata(L, 1); inventory_t *inventory = (inventory_t *)jsUnwrapPointer(args_p[0]);
inventorysort_t sortBy = (inventorysort_t)lua_tonumber(L, 2); inventorysort_t sortBy = (inventorysort_t)jerry_value_as_number(args_p[1]);
assertNotNull(inventory, "Inventory pointer cannot be NULL."); assertNotNull(inventory, "Inventory pointer cannot be NULL.");
inventorySort(inventory, sortBy, reverse); inventorySort(inventory, sortBy, reverse);
return 0; return jerry_undefined();
} }
static void moduleItem(lua_State *L) { static void moduleItem(void) {
assertNotNull(L, "Lua state cannot be NULL"); jsEvalStr(ITEM_SCRIPT);
luaL_dostring(L, ITEM_SCRIPT); jerry_value_t backpack = jsWrapPointer(&BACKPACK);
jsSetValue("BACKPACK", backpack);
jerry_value_free(backpack);
lua_pushlightuserdata(L, &BACKPACK); jsRegister("inventoryItemExists", moduleInventoryItemExists);
lua_setglobal(L, "BACKPACK"); jsRegister("inventoryAdd", moduleInventoryAdd);
jsRegister("inventorySet", moduleInventorySet);
lua_register(L, "inventoryItemExists", moduleInventoryItemExists); jsRegister("inventoryRemove", moduleInventoryRemove);
lua_register(L, "inventoryAdd", moduleInventoryAdd); jsRegister("inventoryGetCount", moduleInventoryGetCount);
lua_register(L, "inventorySet", moduleInventorySet); jsRegister("inventoryIsFull", moduleInventoryIsFull);
lua_register(L, "inventoryRemove", moduleInventoryRemove); jsRegister("inventoryItemFull", moduleInventoryItemFull);
lua_register(L, "inventoryGetCount", moduleInventoryGetCount); jsRegister("inventorySort", moduleInventorySort);
lua_register(L, "inventoryIsFull", moduleInventoryIsFull);
lua_register(L, "inventoryItemFull", moduleInventoryItemFull);
lua_register(L, "inventorySort", moduleInventorySort);
} }
@@ -6,96 +6,58 @@
*/ */
#pragma once #pragma once
#include "script/scriptcontext.h" #include "script/module/modulebase.h"
#include "assert/assert.h" #include "assert/assert.h"
#include "story/storyflag.h" #include "story/storyflag.h"
static int moduleStoryFlagGet(lua_State *L) { JS_FUNC(moduleStoryFlagGet) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) { storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
luaL_error(L, "Expected flag ID.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag); return JS_THROW("storyFlagGet: invalid flag ID");
return 0;
} }
return jerry_number((double)storyFlagGet(flag));
storyflagvalue_t value = storyFlagGet(flag);
lua_pushnumber(L, value);
return 1;
} }
static int moduleStoryFlagSet(lua_State *L) { JS_FUNC(moduleStoryFlagSet) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(2);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) { JS_REQUIRE_NUMBER(1);
luaL_error(L, "Expected flag ID."); storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Expected flag value.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag); return JS_THROW("storyFlagSet: invalid flag ID");
return 0;
} }
storyflagvalue_t value = (storyflagvalue_t)jerry_value_as_number(args_p[1]);
storyflagvalue_t value = (storyflagvalue_t)lua_tonumber(L, 2);
storyFlagSet(flag, value); storyFlagSet(flag, value);
return 0; return jerry_undefined();
} }
static int moduleStoryFlagIncrement(lua_State *L) { JS_FUNC(moduleStoryFlagIncrement) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) { storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
luaL_error(L, "Expected flag ID.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag); return JS_THROW("storyFlagIncrement: invalid flag ID");
return 0;
} }
storyFlagSet(flag, storyFlagGet(flag) + 1);
storyflagvalue_t value = storyFlagGet(flag); return jerry_undefined();
storyFlagSet(flag, value + 1);
return 0;
} }
static int moduleStoryFlagDecrement(lua_State *L) { JS_FUNC(moduleStoryFlagDecrement) {
assertNotNull(L, "Lua state cannot be NULL"); JS_REQUIRE_ARGS(1);
JS_REQUIRE_NUMBER(0);
if(!lua_isnumber(L, 1)) { storyflag_t flag = (storyflag_t)jerry_value_as_number(args_p[0]);
luaL_error(L, "Expected flag ID.");
return 0;
}
storyflag_t flag = (storyflag_t)lua_tonumber(L, 1);
if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) { if(flag <= STORY_FLAG_NULL || flag >= STORY_FLAG_COUNT) {
luaL_error(L, "Invalid flag ID %d", flag); return JS_THROW("storyFlagDecrement: invalid flag ID");
return 0;
} }
storyFlagSet(flag, storyFlagGet(flag) - 1);
storyflagvalue_t value = storyFlagGet(flag); return jerry_undefined();
storyFlagSet(flag, value - 1);
return 0;
} }
static void moduleStoryFlag(lua_State *L) { static void moduleStoryFlag(void) {
assertNotNull(L, "Lua state cannot be NULL"); jsRegister("storyFlagGet", moduleStoryFlagGet);
jsRegister("storyFlagSet", moduleStoryFlagSet);
lua_register(L, "storyFlagGet", moduleStoryFlagGet); jsRegister("storyFlagIncrement", moduleStoryFlagIncrement);
lua_register(L, "storyFlagSet", moduleStoryFlagSet); jsRegister("storyFlagDecrement", moduleStoryFlagDecrement);
lua_register(L, "storyFlagIncrement", moduleStoryFlagIncrement);
lua_register(L, "storyFlagDecrement", moduleStoryFlagDecrement);
} }
+7 -7
View File
@@ -45,7 +45,7 @@ out = [
"", "",
] ]
lua = [] js = []
for name, (r, g, b, a) in colors.items(): for name, (r, g, b, a) in colors.items():
r8, g8, b8, a8 = (int(float(ch) * 255) for ch in (r, g, b, a)) r8, g8, b8, a8 = (int(float(ch) * 255) for ch in (r, g, b, a))
macro = "COLOR_" + name.upper() macro = "COLOR_" + name.upper()
@@ -60,16 +60,16 @@ for name, (r, g, b, a) in colors.items():
f"#define {macro} {macro}_4B", f"#define {macro} {macro}_4B",
"", "",
] ]
lua += [ js += [
f"function color{camel}()", f"function color{camel}() {{",
f" return color({r8}, {g8}, {b8}, {a8})", f" return color({r8}, {g8}, {b8}, {a8});",
"end", "}",
"", "",
] ]
out.append("// Lua color functions") out.append("// JS color functions")
out.append("#define COLOR_SCRIPT \\") out.append("#define COLOR_SCRIPT \\")
for line in "\n".join(lua).rstrip().splitlines(): for line in "\n".join(js).rstrip().splitlines():
out.append(f' "{line}\\n" \\') out.append(f' "{line}\\n" \\')
out[-1] = out[-1].rstrip(" \\") out[-1] = out[-1].rstrip(" \\")
out.append("") out.append("")
+1 -1
View File
@@ -50,7 +50,7 @@ out += [
"static const char_t *INPUT_ACTION_SCRIPT =", "static const char_t *INPUT_ACTION_SCRIPT =",
] ]
for input_id in input_ids: for input_id in input_ids:
out.append(f" \"{id_enum(input_id)} = {id_values[input_id]}\\n\"") out.append(f" \"var {id_enum(input_id)} = {id_values[input_id]};\\n\"")
out += [";", ""] out += [";", ""]
os.makedirs(os.path.dirname(args.output), exist_ok=True) os.makedirs(os.path.dirname(args.output), exist_ok=True)