Compare commits
29 Commits
http
...
playertest
| Author | SHA1 | Date | |
|---|---|---|---|
| d373de7a29 | |||
| 1efa9a9f7b | |||
| 0fb3ba2f91 | |||
| 3b4c5b5153 | |||
| 9293aeeec8 | |||
| 03ae83b119 | |||
| abd63cc6cf | |||
| 2e43aa2c44 | |||
| 3d984e13c2 | |||
| 010900fe21 | |||
| ffed626447 | |||
| 61f69af35a | |||
| bd248ee91c | |||
| 194255bffe | |||
| 52ee627079 | |||
| bd4200e707 | |||
| 73e7d6c7f3 | |||
| a41b0e916b | |||
| 19f2a2c616 | |||
| 998601f722 | |||
| 7c3386cf3e | |||
| d161182997 | |||
| 1646dc2dbd | |||
| b640295be2 | |||
| b89ae2391b | |||
| a0fad441d0 | |||
| 340084dac3 | |||
| c78135aa09 | |||
| d19f8bbd30 |
+15
-15
@@ -53,21 +53,21 @@ jobs:
|
|||||||
path: ./git-artifcats/Dusk
|
path: ./git-artifcats/Dusk
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-vita:
|
# build-vita:
|
||||||
runs-on: ubuntu-latest
|
# runs-on: ubuntu-latest
|
||||||
steps:
|
# steps:
|
||||||
- name: Checkout repository
|
# - name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
# uses: actions/checkout@v6
|
||||||
- name: Set up Docker
|
# - name: Set up Docker
|
||||||
uses: docker/setup-docker-action@v5
|
# uses: docker/setup-docker-action@v5
|
||||||
- name: Build Vita
|
# - name: Build Vita
|
||||||
run: ./scripts/build-vita-docker.sh
|
# run: ./scripts/build-vita-docker.sh
|
||||||
- name: Upload Vita binary
|
# - name: Upload Vita binary
|
||||||
uses: actions/upload-artifact@v6
|
# uses: actions/upload-artifact@v6
|
||||||
with:
|
# with:
|
||||||
name: dusk-vita
|
# name: dusk-vita
|
||||||
path: build-vita/Dusk.vpk
|
# path: build-vita/Dusk.vpk
|
||||||
if-no-files-found: error
|
# if-no-files-found: error
|
||||||
|
|
||||||
build-knulli:
|
build-knulli:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
+3
-1
@@ -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 3.13)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
var OverworldEntity = include('entities/OverworldEntity.js');
|
||||||
|
|
||||||
|
function CubeEntity() {
|
||||||
|
OverworldEntity.call(this);
|
||||||
|
|
||||||
|
this.add(MESH);
|
||||||
|
this.add(MATERIAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CubeEntity.prototype = Object.create(OverworldEntity.prototype);
|
||||||
|
CubeEntity.prototype.constructor = CubeEntity;
|
||||||
|
|
||||||
|
CubeEntity.prototype.update = function() {
|
||||||
|
OverworldEntity.prototype.update.call(this);
|
||||||
|
var speed = 3.0;
|
||||||
|
var move = Input.axis2D(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, INPUT_ACTION_UP, INPUT_ACTION_DOWN);
|
||||||
|
this.position.position.x += move.x * speed * TIME.delta;
|
||||||
|
this.position.position.z += move.y * speed * TIME.delta;
|
||||||
|
this.material.setColor(Color.rainbow());
|
||||||
|
};
|
||||||
|
|
||||||
|
module = CubeEntity;
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
function OverworldEntity() {
|
||||||
|
Entity.call(this);
|
||||||
|
|
||||||
|
this.add(POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
OverworldEntity.prototype = Object.create(Entity.prototype);
|
||||||
|
OverworldEntity.prototype.constructor = OverworldEntity;
|
||||||
|
|
||||||
|
OverworldEntity.prototype.update = function() {
|
||||||
|
// var speed = 3.0;
|
||||||
|
// var move = Input.axis2D(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, INPUT_ACTION_UP, INPUT_ACTION_DOWN);
|
||||||
|
// this.position.position.x += move.x * speed * TIME.delta;
|
||||||
|
// this.position.position.z += move.y * speed * TIME.delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
OverworldEntity.prototype.dispose = function() {
|
||||||
|
// Nothing to dispose
|
||||||
|
}
|
||||||
|
|
||||||
|
module = OverworldEntity;
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
Console.visible = true;
|
||||||
|
|
||||||
|
// Default input bindings.
|
||||||
|
if (typeof PSP !== 'undefined') {
|
||||||
|
Input.bind("up", INPUT_ACTION_UP);
|
||||||
|
Input.bind("down", INPUT_ACTION_DOWN);
|
||||||
|
Input.bind("left", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("right", INPUT_ACTION_RIGHT);
|
||||||
|
Input.bind("accept", INPUT_ACTION_ACCEPT);
|
||||||
|
Input.bind("cancel", INPUT_ACTION_CANCEL);
|
||||||
|
Input.bind("select", INPUT_ACTION_RAGEQUIT);
|
||||||
|
Input.bind("lstick_up", INPUT_ACTION_UP);
|
||||||
|
Input.bind("lstick_down", INPUT_ACTION_DOWN);
|
||||||
|
Input.bind("lstick_left", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("lstick_right", INPUT_ACTION_RIGHT);
|
||||||
|
Input.bind("triangle", INPUT_ACTION_CONSOLE);
|
||||||
|
|
||||||
|
} else if (typeof DOLPHIN !== 'undefined') {
|
||||||
|
Input.bind("up", INPUT_ACTION_UP);
|
||||||
|
Input.bind("down", INPUT_ACTION_DOWN);
|
||||||
|
Input.bind("left", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("right", INPUT_ACTION_RIGHT);
|
||||||
|
Input.bind("b", INPUT_ACTION_CANCEL);
|
||||||
|
Input.bind("a", INPUT_ACTION_ACCEPT);
|
||||||
|
Input.bind("z", INPUT_ACTION_CONSOLE);
|
||||||
|
Input.bind("lstick_up", INPUT_ACTION_UP);
|
||||||
|
Input.bind("lstick_down", INPUT_ACTION_DOWN);
|
||||||
|
Input.bind("lstick_left", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("lstick_right", INPUT_ACTION_RIGHT);
|
||||||
|
|
||||||
|
} else if (typeof LINUX !== 'undefined') {
|
||||||
|
if (typeof INPUT_KEYBOARD !== 'undefined') {
|
||||||
|
Input.bind("w", INPUT_ACTION_UP);
|
||||||
|
Input.bind("s", INPUT_ACTION_DOWN);
|
||||||
|
Input.bind("a", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("d", INPUT_ACTION_RIGHT);
|
||||||
|
|
||||||
|
Input.bind("left", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("right", INPUT_ACTION_RIGHT);
|
||||||
|
Input.bind("up", INPUT_ACTION_UP);
|
||||||
|
Input.bind("down", INPUT_ACTION_DOWN);
|
||||||
|
|
||||||
|
Input.bind("enter", INPUT_ACTION_ACCEPT);
|
||||||
|
Input.bind("e", INPUT_ACTION_ACCEPT);
|
||||||
|
|
||||||
|
Input.bind("q", INPUT_ACTION_CANCEL);
|
||||||
|
|
||||||
|
Input.bind("escape", INPUT_ACTION_RAGEQUIT);
|
||||||
|
Input.bind("`", INPUT_ACTION_CONSOLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof INPUT_GAMEPAD !== 'undefined') {
|
||||||
|
Input.bind("gamepad_up", INPUT_ACTION_UP);
|
||||||
|
Input.bind("gamepad_down", INPUT_ACTION_DOWN);
|
||||||
|
Input.bind("gamepad_left", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("gamepad_right", INPUT_ACTION_RIGHT);
|
||||||
|
|
||||||
|
Input.bind("gamepad_a", INPUT_ACTION_ACCEPT);
|
||||||
|
Input.bind("gamepad_b", INPUT_ACTION_CANCEL);
|
||||||
|
Input.bind("gamepad_back", INPUT_ACTION_RAGEQUIT);
|
||||||
|
|
||||||
|
Input.bind("gamepad_lstick_up", INPUT_ACTION_UP);
|
||||||
|
Input.bind("gamepad_lstick_down", INPUT_ACTION_DOWN);
|
||||||
|
Input.bind("gamepad_lstick_left", INPUT_ACTION_LEFT);
|
||||||
|
Input.bind("gamepad_lstick_right", INPUT_ACTION_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof INPUT_POINTER !== 'undefined') {
|
||||||
|
Input.bind("mouse_x", INPUT_ACTION_POINTERX);
|
||||||
|
Input.bind("mouse_y", INPUT_ACTION_POINTERY);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
print("Unknown platform, no default input bindings set.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene.set('scenes/cube.js');
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
module('input')
|
|
||||||
module('platform')
|
|
||||||
module('scene')
|
|
||||||
module('locale')
|
|
||||||
|
|
||||||
-- 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)
|
|
||||||
|
|
||||||
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_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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
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
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function CubeScene() {
|
||||||
|
this.cam = new Entity();
|
||||||
|
this.cam.add(POSITION);
|
||||||
|
this.cam.add(CAMERA);
|
||||||
|
|
||||||
|
this.cam.position.position = new Vec3(3, 3, 3);
|
||||||
|
this.cam.position.lookAt(new Vec3(0, 0, 0));
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
|
||||||
|
this.spriteEnt = new Entity();
|
||||||
|
this.spriteEnt.add(POSITION);
|
||||||
|
this.spriteEnt.position.position = new Vec3(16, 16, 0);
|
||||||
|
// this.spriteEnt.sprite.setTexture('ui/minogram.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
CubeScene.prototype = Object.create(Scene.prototype);
|
||||||
|
CubeScene.prototype.constructor = CubeScene;
|
||||||
|
|
||||||
|
CubeScene.prototype.update = function() {
|
||||||
|
this.cube.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
CubeScene.prototype.dispose = function() {
|
||||||
|
this.cam.dispose();
|
||||||
|
this.cube.dispose();
|
||||||
|
this.spriteEnt.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = CubeScene;
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Turn things off we don't need
|
||||||
|
set(JERRY_CMDLINE OFF CACHE BOOL "" FORCE)
|
||||||
|
set(JERRY_EXT ON CACHE BOOL "" FORCE)
|
||||||
|
set(JERRY_DEBUGGER OFF CACHE BOOL "" FORCE)
|
||||||
|
set(JERRY_BUILTIN_DATE OFF CACHE BOOL "" FORCE)
|
||||||
|
set(ENABLE_LTO OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
# Fetch Jerry
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
jerryscript
|
||||||
|
GIT_REPOSITORY https://git.wish.moe/YourWishes/jerryscript
|
||||||
|
GIT_TAG float32-fix
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(jerryscript)
|
||||||
|
|
||||||
|
# Mark found
|
||||||
|
set(jerryscript_FOUND ON)
|
||||||
|
|
||||||
|
# Define targets
|
||||||
|
if(TARGET jerryscript-core)
|
||||||
|
set(JERRY_CORE_TARGET jerryscript-core)
|
||||||
|
elseif(TARGET jerry-core)
|
||||||
|
set(JERRY_CORE_TARGET jerry-core)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TARGET jerryscript-ext)
|
||||||
|
set(JERRY_EXT_TARGET jerryscript-ext)
|
||||||
|
elseif(TARGET jerry-ext)
|
||||||
|
set(JERRY_EXT_TARGET jerry-ext)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(TARGET jerryscript-port-default)
|
||||||
|
set(JERRY_PORT_TARGET jerryscript-port-default)
|
||||||
|
elseif(TARGET jerry-port-default)
|
||||||
|
set(JERRY_PORT_TARGET jerry-port-default)
|
||||||
|
elseif(TARGET jerryscript-port)
|
||||||
|
set(JERRY_PORT_TARGET jerryscript-port)
|
||||||
|
elseif(TARGET jerry-port)
|
||||||
|
set(JERRY_PORT_TARGET jerry-port)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT JERRY_CORE_TARGET)
|
||||||
|
message(FATAL_ERROR "JerryScript core target not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT JERRY_EXT_TARGET)
|
||||||
|
message(FATAL_ERROR "JerryScript ext target not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT JERRY_PORT_TARGET)
|
||||||
|
message(FATAL_ERROR "JerryScript port target not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(tgt IN ITEMS
|
||||||
|
${JERRY_CORE_TARGET}
|
||||||
|
${JERRY_EXT_TARGET}
|
||||||
|
${JERRY_PORT_TARGET}
|
||||||
|
)
|
||||||
|
if(TARGET ${tgt})
|
||||||
|
set_property(TARGET ${tgt} PROPERTY INTERPROCEDURAL_OPTIMIZATION OFF)
|
||||||
|
target_compile_definitions(${JERRY_CORE_TARGET} PRIVATE
|
||||||
|
JERRY_NUMBER_TYPE_FLOAT64=0
|
||||||
|
JERRY_BUILTIN_DATE=0
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Export include dirs through the targets
|
||||||
|
target_include_directories(${JERRY_CORE_TARGET} INTERFACE
|
||||||
|
${jerryscript_SOURCE_DIR}/jerry-core/include
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${JERRY_EXT_TARGET} INTERFACE
|
||||||
|
${jerryscript_SOURCE_DIR}/jerry-ext/include
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${JERRY_PORT_TARGET} INTERFACE
|
||||||
|
${jerryscript_SOURCE_DIR}/jerry-port/default/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# Suppress JerryScript-only warning
|
||||||
|
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
||||||
|
target_compile_options(${JERRY_CORE_TARGET} PRIVATE
|
||||||
|
-Wno-error
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(jerryscript::core ALIAS ${JERRY_CORE_TARGET})
|
||||||
|
add_library(jerryscript::ext ALIAS ${JERRY_EXT_TARGET})
|
||||||
|
add_library(jerryscript::port ALIAS ${JERRY_PORT_TARGET})
|
||||||
@@ -4,6 +4,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
DUSK_INPUT_GAMEPAD
|
DUSK_INPUT_GAMEPAD
|
||||||
DUSK_DISPLAY_WIDTH=640
|
DUSK_DISPLAY_WIDTH=640
|
||||||
DUSK_DISPLAY_HEIGHT=480
|
DUSK_DISPLAY_HEIGHT=480
|
||||||
|
DUSK_THREAD_PTHREAD
|
||||||
)
|
)
|
||||||
|
|
||||||
# Custom compiler flags
|
# Custom compiler flags
|
||||||
@@ -21,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 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} PRIVATE
|
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||||
cglm
|
cglm
|
||||||
liblua
|
|
||||||
m
|
m
|
||||||
fat
|
fat
|
||||||
PkgConfig::zip
|
PkgConfig::zip
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||||
DUSK_SDL2
|
DUSK_SDL2
|
||||||
DUSK_OPENGL
|
DUSK_OPENGL
|
||||||
|
DUSK_CONSOLE_POSIX
|
||||||
# DUSK_OPENGL_LEGACY
|
# DUSK_OPENGL_LEGACY
|
||||||
DUSK_LINUX
|
DUSK_LINUX
|
||||||
DUSK_DISPLAY_SIZE_DYNAMIC
|
DUSK_DISPLAY_SIZE_DYNAMIC
|
||||||
@@ -41,5 +42,5 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
DUSK_INPUT_GAMEPAD
|
DUSK_INPUT_GAMEPAD
|
||||||
DUSK_TIME_DYNAMIC
|
DUSK_TIME_DYNAMIC
|
||||||
DUSK_NETWORK_IPV6
|
DUSK_NETWORK_IPV6
|
||||||
THREAD_PTHREAD=1
|
DUSK_THREAD_PTHREAD
|
||||||
)
|
)
|
||||||
+21
-4
@@ -1,6 +1,19 @@
|
|||||||
|
# Fixes some problems building JerryScript
|
||||||
|
set(CMAKE_AR "$ENV{PSPDEV}/bin/psp-ar" CACHE FILEPATH "" FORCE)
|
||||||
|
set(CMAKE_RANLIB "$ENV{PSPDEV}/bin/psp-ranlib" CACHE FILEPATH "" FORCE)
|
||||||
|
set(CMAKE_C_COMPILER_AR "$ENV{PSPDEV}/bin/psp-ar" CACHE FILEPATH "" FORCE)
|
||||||
|
set(CMAKE_C_COMPILER_RANLIB "$ENV{PSPDEV}/bin/psp-ranlib" CACHE FILEPATH "" FORCE)
|
||||||
|
set(CMAKE_C_ARCHIVE_CREATE "$ENV{PSPDEV}/bin/psp-ar qc <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||||
|
set(CMAKE_C_ARCHIVE_APPEND "$ENV{PSPDEV}/bin/psp-ar q <TARGET> <LINK_FLAGS> <OBJECTS>")
|
||||||
|
set(CMAKE_C_ARCHIVE_FINISH "$ENV{PSPDEV}/bin/psp-ranlib <TARGET>")
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF CACHE BOOL "" FORCE)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_C OFF CACHE BOOL "" FORCE)
|
||||||
|
set(JERRY_LTO OFF CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
find_package(jerryscript REQUIRED)
|
||||||
find_package(SDL2 REQUIRED)
|
find_package(SDL2 REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
target_link_libraries(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||||
${SDL2_LIBRARIES}
|
${SDL2_LIBRARIES}
|
||||||
SDL2
|
SDL2
|
||||||
pthread
|
pthread
|
||||||
@@ -29,13 +42,17 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
pspnet_apctl
|
pspnet_apctl
|
||||||
psphttp
|
psphttp
|
||||||
pspssl
|
pspssl
|
||||||
|
|
||||||
|
jerryscript::core
|
||||||
|
jerryscript::ext
|
||||||
|
jerryscript::port
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
target_include_directories(${DUSK_BINARY_TARGET_NAME} PRIVATE
|
||||||
${SDL2_INCLUDE_DIRS}
|
${SDL2_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||||
DUSK_SDL2
|
DUSK_SDL2
|
||||||
DUSK_OPENGL
|
DUSK_OPENGL
|
||||||
DUSK_PSP
|
DUSK_PSP
|
||||||
@@ -44,7 +61,7 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
|||||||
DUSK_OPENGL_LEGACY
|
DUSK_OPENGL_LEGACY
|
||||||
DUSK_DISPLAY_WIDTH=480
|
DUSK_DISPLAY_WIDTH=480
|
||||||
DUSK_DISPLAY_HEIGHT=272
|
DUSK_DISPLAY_HEIGHT=272
|
||||||
THREAD_PTHREAD=1
|
DUSK_THREAD_PTHREAD
|
||||||
)
|
)
|
||||||
|
|
||||||
# Postbuild, create .pbp file for PSP.
|
# Postbuild, create .pbp file for PSP.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+10
-14
@@ -32,18 +32,13 @@ 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} PRIVATE
|
||||||
add_library(Lua::Lua INTERFACE IMPORTED)
|
jerryscript::core
|
||||||
set_target_properties(
|
jerryscript::ext
|
||||||
Lua::Lua
|
jerryscript::port
|
||||||
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
|
||||||
@@ -61,8 +56,9 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
|
|||||||
# Subdirs
|
# Subdirs
|
||||||
add_subdirectory(assert)
|
add_subdirectory(assert)
|
||||||
add_subdirectory(asset)
|
add_subdirectory(asset)
|
||||||
add_subdirectory(log)
|
add_subdirectory(console)
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
|
add_subdirectory(log)
|
||||||
add_subdirectory(engine)
|
add_subdirectory(engine)
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
add_subdirectory(error)
|
add_subdirectory(error)
|
||||||
@@ -77,4 +73,4 @@ add_subdirectory(time)
|
|||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
add_subdirectory(network)
|
add_subdirectory(network)
|
||||||
add_subdirectory(util)
|
add_subdirectory(util)
|
||||||
# add_subdirectory(thread)
|
add_subdirectory(thread)
|
||||||
@@ -25,7 +25,7 @@ errorret_t assetInit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool_t assetFileExists(const char_t *filename) {
|
bool_t assetFileExists(const char_t *filename) {
|
||||||
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
|
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long.");
|
||||||
|
|
||||||
zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0);
|
zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0);
|
||||||
if(idx < 0) return false;
|
if(idx < 0) return false;
|
||||||
@@ -38,7 +38,7 @@ errorret_t assetLoad(
|
|||||||
void *params,
|
void *params,
|
||||||
void *output
|
void *output
|
||||||
) {
|
) {
|
||||||
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
|
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long.");
|
||||||
assertNotNull(output, "Output pointer cannot be NULL.");
|
assertNotNull(output, "Output pointer cannot be NULL.");
|
||||||
assertNotNull(loader, "Asset file loader cannot be NULL.");
|
assertNotNull(loader, "Asset file loader cannot be NULL.");
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
#include "error/error.h"
|
#include "error/error.h"
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
|
|
||||||
|
#define ASSET_FILE_PATH_MAX FILENAME_MAX
|
||||||
|
|
||||||
typedef struct assetfile_s assetfile_t;
|
typedef struct assetfile_s assetfile_t;
|
||||||
|
|
||||||
typedef errorret_t (*assetfileloader_t)(assetfile_t *file);
|
typedef errorret_t (*assetfileloader_t)(assetfile_t *file);
|
||||||
|
|||||||
@@ -1,82 +1,92 @@
|
|||||||
/**
|
/**
|
||||||
* 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,
|
||||||
|
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");
|
|
||||||
|
|
||||||
assetscript_t script;
|
|
||||||
script.ctx = ctx;
|
|
||||||
|
|
||||||
return assetLoad(
|
assetscript_t scriptData;
|
||||||
path,
|
scriptData.resultOut = resultOut;
|
||||||
assetScriptLoader,
|
|
||||||
NULL,
|
return assetLoad(path, assetScriptLoader, NULL, &scriptData);
|
||||||
&script
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,28 +1,23 @@
|
|||||||
/**
|
/**
|
||||||
* 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 "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
#define ASSET_SCRIPT_BUFFER_SIZE 1024
|
#define ASSET_SCRIPT_CHUNK_SIZE 1024
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void *nothing;
|
jerry_value_t *resultOut;
|
||||||
} assetscriptloaderparams_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
scriptcontext_t *ctx;
|
|
||||||
char_t buffer[ASSET_SCRIPT_BUFFER_SIZE];
|
|
||||||
} 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 +25,14 @@ 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 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,
|
||||||
/**
|
jerry_value_t *resultOut
|
||||||
* Reader function for Lua to read script data from the asset.
|
);
|
||||||
*
|
|
||||||
* @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,10 +1,9 @@
|
|||||||
# 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
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
moduleui.c
|
console.c
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "console.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "input/input.h"
|
||||||
|
#include "log/log.h"
|
||||||
|
#include "engine/engine.h"
|
||||||
|
#include "script/scriptmanager.h"
|
||||||
|
#include "display/shader/shaderunlit.h"
|
||||||
|
#include "display/text/text.h"
|
||||||
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
|
||||||
|
console_t CONSOLE;
|
||||||
|
|
||||||
|
void consoleInit(void) {
|
||||||
|
memoryZero(&CONSOLE, sizeof(console_t));
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadInit(&CONSOLE.thread, consoleInputThread);
|
||||||
|
threadMutexInit(&CONSOLE.execMutex);
|
||||||
|
threadMutexInit(&CONSOLE.printMutex);
|
||||||
|
threadStartRequest(&CONSOLE.thread);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void consolePrint(const char_t *message, ...) {
|
||||||
|
char_t buffer[CONSOLE_LINE_MAX];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, message);
|
||||||
|
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexLock(&CONSOLE.printMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memoryMove(
|
||||||
|
CONSOLE.line[0],
|
||||||
|
CONSOLE.line[1],
|
||||||
|
(CONSOLE_HISTORY_MAX - 1) * CONSOLE_LINE_MAX
|
||||||
|
);
|
||||||
|
memoryCopy(CONSOLE.line[CONSOLE_HISTORY_MAX - 1], buffer, len + 1);
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexUnlock(&CONSOLE.printMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
logDebug("%s\n", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleExec(const char_t *line) {
|
||||||
|
assertNotNull(line, "line must not be NULL");
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexLock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
|
||||||
|
"Console exec buffer is full"
|
||||||
|
);
|
||||||
|
|
||||||
|
stringCopy(
|
||||||
|
CONSOLE.execBuffer[CONSOLE.execBufferCount],
|
||||||
|
line,
|
||||||
|
CONSOLE_LINE_MAX
|
||||||
|
);
|
||||||
|
CONSOLE.execBufferCount++;
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexUnlock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleUpdate(void) {
|
||||||
|
#ifdef DUSK_TIME_DYNAMIC
|
||||||
|
if(TIME.dynamicUpdate) return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(inputPressed(INPUT_ACTION_CONSOLE)) {
|
||||||
|
CONSOLE.visible = !CONSOLE.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CONSOLE.execBufferCount == 0) return;
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexLock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char_t execBuffer[CONSOLE_EXEC_BUFFER_MAX][CONSOLE_LINE_MAX];
|
||||||
|
uint32_t execBufferCount = CONSOLE.execBufferCount;
|
||||||
|
memoryCopy(execBuffer, CONSOLE.execBuffer, sizeof(execBuffer));
|
||||||
|
CONSOLE.execBufferCount = 0;
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadMutexUnlock(&CONSOLE.execMutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < execBufferCount; i++) {
|
||||||
|
jerry_value_t result = 0;
|
||||||
|
errorret_t err = scriptManagerExec(execBuffer[i], &result);
|
||||||
|
if(err.code != ERROR_OK) {
|
||||||
|
consolePrint("Error: %s", err.state->message);
|
||||||
|
errorCatch(err);
|
||||||
|
} else if(!jerry_value_is_undefined(result)) {
|
||||||
|
jerry_value_t strVal = jerry_value_to_string(result);
|
||||||
|
char_t buf[CONSOLE_LINE_MAX];
|
||||||
|
jerry_size_t len = jerry_string_to_buffer(
|
||||||
|
strVal, JERRY_ENCODING_UTF8, (jerry_char_t*)buf, sizeof(buf) - 1
|
||||||
|
);
|
||||||
|
buf[len] = '\0';
|
||||||
|
jerry_value_free(strVal);
|
||||||
|
consolePrint("%s", buf);
|
||||||
|
}
|
||||||
|
if(result != 0) jerry_value_free(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t consoleDraw(void) {
|
||||||
|
if(!CONSOLE.visible) errorOk();
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
|
||||||
|
errorChain(textDraw(
|
||||||
|
0, FONT_TILESET_DEFAULT.tileHeight * i,
|
||||||
|
CONSOLE.line[i],
|
||||||
|
COLOR_WHITE,
|
||||||
|
&FONT_TILESET_DEFAULT,
|
||||||
|
&FONT_TEXTURE_DEFAULT
|
||||||
|
));
|
||||||
|
}
|
||||||
|
errorChain(spriteBatchFlush());
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void consoleDispose(void) {
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
threadStop(&CONSOLE.thread);
|
||||||
|
threadMutexDispose(&CONSOLE.execMutex);
|
||||||
|
threadMutexDispose(&CONSOLE.printMutex);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
void consoleInputThread(thread_t *thread) {
|
||||||
|
assertNotNull(thread, "Thread cannot be NULL.");
|
||||||
|
|
||||||
|
char_t line[CONSOLE_LINE_MAX];
|
||||||
|
|
||||||
|
struct pollfd pfd = {
|
||||||
|
.fd = STDIN_FILENO,
|
||||||
|
.events = POLLIN
|
||||||
|
};
|
||||||
|
|
||||||
|
while(!threadShouldStop(thread) && ENGINE.running) {
|
||||||
|
int32_t rc = poll(&pfd, 1, CONSOLE_POSIX_POLL_RATE);
|
||||||
|
|
||||||
|
if(rc == 0) continue;
|
||||||
|
if(rc < 0) {
|
||||||
|
if(errno == EINTR) continue;
|
||||||
|
assertUnreachable("poll() failed with unexpected error.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) break;
|
||||||
|
if(!(pfd.revents & POLLIN)) {
|
||||||
|
pfd.revents = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!fgets(line, CONSOLE_LINE_MAX, stdin)) {
|
||||||
|
if(feof(stdin)) break;
|
||||||
|
clearerr(stdin);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(line);
|
||||||
|
while(len && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
|
||||||
|
line[--len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if(len > 0) consoleExec(line);
|
||||||
|
|
||||||
|
pfd.revents = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "consoledefs.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
#include "thread/thread.h"
|
||||||
|
#include <poll.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#define CONSOLE_POSIX_POLL_RATE 75
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_t line[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX];
|
||||||
|
bool_t visible;
|
||||||
|
|
||||||
|
char_t execBuffer[CONSOLE_EXEC_BUFFER_MAX][CONSOLE_LINE_MAX];
|
||||||
|
uint32_t execBufferCount;
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
thread_t thread;
|
||||||
|
threadmutex_t execMutex;
|
||||||
|
threadmutex_t printMutex;
|
||||||
|
#endif
|
||||||
|
} console_t;
|
||||||
|
|
||||||
|
extern console_t CONSOLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the console.
|
||||||
|
*/
|
||||||
|
void consoleInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a message to the console history.
|
||||||
|
*
|
||||||
|
* @param message The message to print (printf-style).
|
||||||
|
*/
|
||||||
|
void consolePrint(const char_t *message, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queues a JS string for execution on the main thread. Thread-safe.
|
||||||
|
*
|
||||||
|
* @param line The JS source line to execute.
|
||||||
|
*/
|
||||||
|
void consoleExec(const char_t *line);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes pending queued script lines. Call once per frame from main thread.
|
||||||
|
*/
|
||||||
|
void consoleUpdate(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the console history to the screen (UI space).
|
||||||
|
*
|
||||||
|
* @return The error return value.
|
||||||
|
*/
|
||||||
|
errorret_t consoleDraw(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the console.
|
||||||
|
*/
|
||||||
|
void consoleDispose(void);
|
||||||
|
|
||||||
|
#ifdef DUSK_CONSOLE_POSIX
|
||||||
|
/**
|
||||||
|
* Input thread handler for POSIX stdin.
|
||||||
|
*
|
||||||
|
* @param thread The thread that is running.
|
||||||
|
*/
|
||||||
|
void consoleInputThread(thread_t *thread);
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CONSOLE_LINE_MAX 512
|
||||||
|
#define CONSOLE_HISTORY_MAX 16
|
||||||
|
#define CONSOLE_EXEC_BUFFER_MAX 32
|
||||||
@@ -25,8 +25,6 @@
|
|||||||
#include "display/shader/shaderunlit.h"
|
#include "display/shader/shaderunlit.h"
|
||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
|
|
||||||
#include "script/module/display/moduleshader.h"
|
|
||||||
|
|
||||||
display_t DISPLAY = { 0 };
|
display_t DISPLAY = { 0 };
|
||||||
|
|
||||||
errorret_t displayInit(void) {
|
errorret_t displayInit(void) {
|
||||||
|
|||||||
@@ -13,19 +13,19 @@
|
|||||||
#include "asset/loader/display/assettilesetloader.h"
|
#include "asset/loader/display/assettilesetloader.h"
|
||||||
#include "display/shader/shaderunlit.h"
|
#include "display/shader/shaderunlit.h"
|
||||||
|
|
||||||
texture_t DEFAULT_FONT_TEXTURE;
|
texture_t FONT_TEXTURE_DEFAULT;
|
||||||
tileset_t DEFAULT_FONT_TILESET;
|
tileset_t FONT_TILESET_DEFAULT;
|
||||||
|
|
||||||
errorret_t textInit(void) {
|
errorret_t textInit(void) {
|
||||||
errorChain(assetTextureLoad(
|
errorChain(assetTextureLoad(
|
||||||
"ui/minogram.png", &DEFAULT_FONT_TEXTURE, TEXTURE_FORMAT_RGBA
|
"ui/minogram.png", &FONT_TEXTURE_DEFAULT, TEXTURE_FORMAT_RGBA
|
||||||
));
|
));
|
||||||
errorChain(assetTilesetLoad("ui/minogram.dtf", &DEFAULT_FONT_TILESET));
|
errorChain(assetTilesetLoad("ui/minogram.dtf", &FONT_TILESET_DEFAULT));
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t textDispose(void) {
|
errorret_t textDispose(void) {
|
||||||
errorChain(textureDispose(&DEFAULT_FONT_TEXTURE));
|
errorChain(textureDispose(&FONT_TEXTURE_DEFAULT));
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
|
|
||||||
#define TEXT_CHAR_START '!'
|
#define TEXT_CHAR_START '!'
|
||||||
|
|
||||||
extern texture_t DEFAULT_FONT_TEXTURE;
|
extern texture_t FONT_TEXTURE_DEFAULT;
|
||||||
extern tileset_t DEFAULT_FONT_TILESET;
|
extern tileset_t FONT_TILESET_DEFAULT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the text system.
|
* Initializes the text system.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <cglm/cglm.h>
|
#include <cglm/cglm.h>
|
||||||
#include <cglm/types.h>
|
#include <cglm/types.h>
|
||||||
#include <cglm/vec2.h>
|
#include <cglm/vec2.h>
|
||||||
|
#include <jerryscript.h>
|
||||||
|
|
||||||
#include "duskplatform.h"
|
#include "duskplatform.h"
|
||||||
|
|
||||||
|
|||||||
+23
-141
@@ -21,76 +21,20 @@
|
|||||||
#include "game/game.h"
|
#include "game/game.h"
|
||||||
#include "physics/physicsmanager.h"
|
#include "physics/physicsmanager.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include "network/networkinfo.h"
|
|
||||||
#include "system/system.h"
|
#include "system/system.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
#include "display/mesh/cube.h"
|
double jerry_port_current_time(void) {
|
||||||
#include "display/mesh/plane.h"
|
dusktimeepoch_t epoch = timeGetEpoch();
|
||||||
|
return epoch.time * 1000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t jerry_port_local_tza(double unix_ms) {
|
||||||
|
(void) unix_ms;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
entityid_t phBoxEnt;
|
|
||||||
componentid_t phBoxPhys;
|
|
||||||
|
|
||||||
float_t onlineSwapTime = FLT_MAX;
|
|
||||||
|
|
||||||
void goOnline();
|
|
||||||
void goOffline();
|
|
||||||
|
|
||||||
void onNetworkConnected(void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
|
|
||||||
networkinfo_t info = networkGetInfo();
|
|
||||||
if(info.type == NETWORK_TYPE_IPV4) {
|
|
||||||
sceneLog(
|
|
||||||
"Connected to network with IPv4 address: " NETWORK_INFO_FORMAT_IPV4 "\n",
|
|
||||||
info.ipv4.ip[0], info.ipv4.ip[1], info.ipv4.ip[2], info.ipv4.ip[3]
|
|
||||||
);
|
|
||||||
#ifdef DUSK_NETWORK_IPV6
|
|
||||||
} else if(info.type == NETWORK_TYPE_IPV6) {
|
|
||||||
sceneLog(
|
|
||||||
"Connected to network with IPv6 address: " NETWORK_INFO_FORMAT_IPV6 "\n",
|
|
||||||
info.ipv6.ip[0], info.ipv6.ip[1], info.ipv6.ip[2], info.ipv6.ip[3],
|
|
||||||
info.ipv6.ip[4], info.ipv6.ip[5], info.ipv6.ip[6], info.ipv6.ip[7],
|
|
||||||
info.ipv6.ip[8], info.ipv6.ip[9], info.ipv6.ip[10], info.ipv6.ip[11],
|
|
||||||
info.ipv6.ip[12], info.ipv6.ip[13], info.ipv6.ip[14], info.ipv6.ip[15]
|
|
||||||
);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
sceneLog("Network connected, I will disconnect at: %.2f1.\n", onlineSwapTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNetworkFailed(errorret_t error, void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
sceneLog("Failed to connect to network, will try again at %.2f1.\n", onlineSwapTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNetworkDisconnected(errorret_t error, void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
sceneLog("Network disconnected, will go online at %.2f1.\n", onlineSwapTime);
|
|
||||||
errorCatch(errorPrint(error));
|
|
||||||
}
|
|
||||||
|
|
||||||
void onNetworkDisconnectFinished(void *user) {
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
sceneLog("Finished disconnecting from network, will go online at %.2f1.\n", onlineSwapTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
void goOnline() {
|
|
||||||
sceneLog("Going online...\n");
|
|
||||||
networkRequestConnection(
|
|
||||||
onNetworkConnected,
|
|
||||||
onNetworkFailed,
|
|
||||||
onNetworkDisconnected,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void goOffline() {
|
|
||||||
sceneLog("Going offline...\n");
|
|
||||||
networkRequestDisconnection(onNetworkDisconnectFinished, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||||
memoryZero(&ENGINE, sizeof(engine_t));
|
memoryZero(&ENGINE, sizeof(engine_t));
|
||||||
@@ -101,6 +45,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
// Init systems. Order is important.
|
// Init systems. Order is important.
|
||||||
errorChain(systemInit());
|
errorChain(systemInit());
|
||||||
timeInit();
|
timeInit();
|
||||||
|
consoleInit();
|
||||||
errorChain(inputInit());
|
errorChain(inputInit());
|
||||||
errorChain(assetInit());
|
errorChain(assetInit());
|
||||||
errorChain(localeManagerInit());
|
errorChain(localeManagerInit());
|
||||||
@@ -110,97 +55,32 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
errorChain(sceneInit());
|
errorChain(sceneInit());
|
||||||
entityManagerInit();
|
entityManagerInit();
|
||||||
physicsManagerInit();
|
physicsManagerInit();
|
||||||
// errorChain(networkInit());
|
errorChain(networkInit());
|
||||||
errorChain(gameInit());
|
errorChain(gameInit());
|
||||||
|
|
||||||
sceneLog("Init done, going to queue online in 3 seconds...\n");
|
|
||||||
onlineSwapTime = TIME.time + 3.0f;
|
|
||||||
|
|
||||||
// Camera
|
|
||||||
entityid_t cam = entityManagerAdd();
|
|
||||||
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
|
|
||||||
float_t distance = 6.0f;
|
|
||||||
entityPositionLookAt(
|
|
||||||
cam, camPos,
|
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
||||||
(vec3){ distance, distance, distance }
|
|
||||||
);
|
|
||||||
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
|
|
||||||
entityCameraSetZFar(cam, camCam, 100.0f);
|
|
||||||
|
|
||||||
// Floor
|
|
||||||
entityid_t floorEnt = entityManagerAdd();
|
|
||||||
componentid_t floorPos = entityAddComponent(floorEnt, COMPONENT_TYPE_POSITION);
|
|
||||||
componentid_t floorMesh = entityAddComponent(floorEnt, COMPONENT_TYPE_MESH);
|
|
||||||
componentid_t floorMat = entityAddComponent(floorEnt, COMPONENT_TYPE_MATERIAL);
|
|
||||||
componentid_t floorPhys = entityAddComponent(floorEnt, COMPONENT_TYPE_PHYSICS);
|
|
||||||
|
|
||||||
entityPositionSetPosition(floorEnt, floorPos, (vec3){ -5.0f, 0.0f, -5.0f });
|
|
||||||
entityPositionSetScale(floorEnt, floorPos, (vec3){ 10.0f, 1.0f, 10.0f });
|
|
||||||
entityMeshSetMesh(floorEnt, floorMesh, &PLANE_MESH_SIMPLE);
|
|
||||||
entityMaterialGetShaderMaterial(floorEnt, floorMat)->unlit.color = COLOR_GREEN;
|
|
||||||
|
|
||||||
entityphysics_t *floorPhysData = entityPhysicsGet(floorEnt, floorPhys);
|
|
||||||
floorPhysData->type = PHYSICS_BODY_STATIC;
|
|
||||||
floorPhysData->shape.type = PHYSICS_SHAPE_PLANE;
|
|
||||||
floorPhysData->shape.data.plane.normal[0] = 0.0f;
|
|
||||||
floorPhysData->shape.data.plane.normal[1] = 1.0f;
|
|
||||||
floorPhysData->shape.data.plane.normal[2] = 0.0f;
|
|
||||||
floorPhysData->shape.data.plane.distance = 0.0f;
|
|
||||||
|
|
||||||
// Box
|
|
||||||
phBoxEnt = entityManagerAdd();
|
|
||||||
componentid_t boxPos = entityAddComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
|
|
||||||
componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH);
|
|
||||||
componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL);
|
|
||||||
phBoxPhys = entityAddComponent(phBoxEnt, COMPONENT_TYPE_PHYSICS);
|
|
||||||
|
|
||||||
entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE);
|
|
||||||
entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED;
|
|
||||||
entityPositionSetPosition(phBoxEnt, boxPos, (vec3){ 0.0f, 4.0f, 0.0f });
|
|
||||||
|
|
||||||
/* Run the init script. */
|
/* Run the init script. */
|
||||||
scriptcontext_t ctx;
|
consolePrint("Engine initialized");
|
||||||
errorChain(scriptContextInit(&ctx));
|
errorChain(scriptManagerExecFile("init.js", NULL));
|
||||||
errorChain(scriptContextExecFile(&ctx, "init.lua"));
|
|
||||||
scriptContextDispose(&ctx);
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineUpdate(void) {
|
errorret_t engineUpdate(void) {
|
||||||
// errorChain(networkUpdate());
|
// Order here is important.
|
||||||
|
errorChain(networkUpdate());
|
||||||
timeUpdate();
|
timeUpdate();
|
||||||
inputUpdate();
|
inputUpdate();
|
||||||
|
consoleUpdate();
|
||||||
uiUpdate();
|
uiUpdate();
|
||||||
errorChain(sceneUpdate());
|
|
||||||
|
|
||||||
/* Reset the box to its start position on demand. */
|
|
||||||
if(inputIsDown(INPUT_ACTION_ACCEPT)) {
|
|
||||||
componentid_t posComp = entityGetComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
|
|
||||||
entityPositionSetPosition(phBoxEnt, posComp, (vec3){ 0.0f, 4.0f, 0.0f });
|
|
||||||
entityPhysicsSetVelocity(phBoxEnt, phBoxPhys, (vec3){ 0.0f, 0.0f, 0.0f });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step physics: positions are updated directly on POSITION components. */
|
|
||||||
physicsManagerUpdate();
|
physicsManagerUpdate();
|
||||||
|
|
||||||
errorChain(gameUpdate());
|
errorChain(gameUpdate());
|
||||||
errorChain(displayUpdate());
|
errorChain(displayUpdate());
|
||||||
|
|
||||||
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
||||||
|
|
||||||
if(TIME.time >= onlineSwapTime) {
|
// Scene update occurs last because only after rendering would we want to do
|
||||||
onlineSwapTime = FLT_MAX;
|
// scene switching, refer to sceneSet() for information.
|
||||||
if(NETWORK.state == NETWORK_STATE_CONNECTED) {
|
errorChain(sceneUpdate());
|
||||||
goOffline();
|
|
||||||
} else {
|
|
||||||
goOnline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -210,13 +90,15 @@ void engineExit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
// errorChain(networkDispose());
|
|
||||||
sceneDispose();
|
sceneDispose();
|
||||||
errorChain(gameDispose());
|
errorChain(gameDispose());
|
||||||
|
errorChain(networkDispose());
|
||||||
entityManagerDispose();
|
entityManagerDispose();
|
||||||
localeManagerDispose();
|
localeManagerDispose();
|
||||||
uiDispose();
|
uiDispose();
|
||||||
|
consoleDispose();
|
||||||
errorChain(displayDispose());
|
errorChain(displayDispose());
|
||||||
errorChain(assetDispose());
|
errorChain(assetDispose());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,14 @@
|
|||||||
componentdefinition_t COMPONENT_DEFINITIONS[] = {
|
componentdefinition_t COMPONENT_DEFINITIONS[] = {
|
||||||
[COMPONENT_TYPE_NULL] = { 0 },
|
[COMPONENT_TYPE_NULL] = { 0 },
|
||||||
|
|
||||||
#define X(enumName, type, field, iMethod, dMethod) \
|
#define X(enm, type, field, iMethod, dMethod) \
|
||||||
[COMPONENT_TYPE_##enumName] = { .init = iMethod, .dispose = dMethod },
|
[COMPONENT_TYPE_##enm] = { \
|
||||||
|
.enumName = #enm, \
|
||||||
|
.name = #field, \
|
||||||
|
.init = iMethod, \
|
||||||
|
.dispose = dMethod \
|
||||||
|
},
|
||||||
|
|
||||||
#include "componentlist.h"
|
#include "componentlist.h"
|
||||||
#undef X
|
#undef X
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ typedef union {
|
|||||||
} componentdata_t;
|
} componentdata_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
const char_t *enumName;
|
||||||
|
const char_t *name;
|
||||||
void (*init)(const entityid_t, const componentid_t);
|
void (*init)(const entityid_t, const componentid_t);
|
||||||
void (*dispose)(const entityid_t, const componentid_t);
|
void (*dispose)(const entityid_t, const componentid_t);
|
||||||
} componentdefinition_t;
|
} componentdefinition_t;
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
add_subdirectory(physics)
|
add_subdirectory(physics)
|
||||||
|
add_subdirectory(script)
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "entity/entitymanager.h"
|
#include "entity/entitymanager.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
#include "entity/component/display/entityposition.h"
|
||||||
#include "display/framebuffer/framebuffer.h"
|
#include "display/framebuffer/framebuffer.h"
|
||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
|
|
||||||
@@ -19,42 +21,6 @@ void entityCameraInit(const entityid_t ent, const componentid_t comp) {
|
|||||||
cam->perspective.fov = glm_rad(45.0f);
|
cam->perspective.fov = glm_rad(45.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float_t entityCameraGetZNear(const entityid_t ent, const componentid_t comp) {
|
|
||||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
|
||||||
ent, comp, COMPONENT_TYPE_CAMERA
|
|
||||||
);
|
|
||||||
return cam->nearClip;
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityCameraSetZNear(
|
|
||||||
const entityid_t ent,
|
|
||||||
const componentid_t comp,
|
|
||||||
const float_t zNear
|
|
||||||
) {
|
|
||||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
|
||||||
ent, comp, COMPONENT_TYPE_CAMERA
|
|
||||||
);
|
|
||||||
cam->nearClip = zNear;
|
|
||||||
}
|
|
||||||
|
|
||||||
float_t entityCameraGetZFar(const entityid_t ent, const componentid_t comp) {
|
|
||||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
|
||||||
ent, comp, COMPONENT_TYPE_CAMERA
|
|
||||||
);
|
|
||||||
return cam->farClip;
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityCameraSetZFar(
|
|
||||||
const entityid_t ent,
|
|
||||||
const componentid_t comp,
|
|
||||||
const float_t zFar
|
|
||||||
) {
|
|
||||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
|
||||||
ent, comp, COMPONENT_TYPE_CAMERA
|
|
||||||
);
|
|
||||||
cam->farClip = zFar;
|
|
||||||
}
|
|
||||||
|
|
||||||
void entityCameraGetProjection(
|
void entityCameraGetProjection(
|
||||||
const entityid_t ent,
|
const entityid_t ent,
|
||||||
const componentid_t comp,
|
const componentid_t comp,
|
||||||
@@ -92,4 +58,38 @@ void entityCameraGetProjection(
|
|||||||
out
|
out
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entityid_t entityCameraGetCurrent(void) {
|
||||||
|
entityid_t camEnts[ENTITY_COUNT_MAX];
|
||||||
|
componentid_t camComps[ENTITY_COUNT_MAX];
|
||||||
|
entityid_t count = componentGetEntitiesWithComponent(
|
||||||
|
COMPONENT_TYPE_CAMERA, camEnts, camComps
|
||||||
|
);
|
||||||
|
if(count == 0) return ENTITY_COUNT_MAX;
|
||||||
|
return camEnts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void entityCameraGetForward(const entityid_t entityId, vec2 out) {
|
||||||
|
componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION);
|
||||||
|
entityposition_t *pos = entityPositionGet(entityId, posComp);
|
||||||
|
// View matrix column layout: M[col][row], forward = {-M[0][2], -M[1][2], -M[2][2]}
|
||||||
|
float_t fx = -pos->transform[0][2];
|
||||||
|
float_t fz = -pos->transform[2][2];
|
||||||
|
float_t len = sqrtf(fx * fx + fz * fz);
|
||||||
|
if(len > 1e-6f) { fx /= len; fz /= len; }
|
||||||
|
out[0] = fx;
|
||||||
|
out[1] = fz;
|
||||||
|
}
|
||||||
|
|
||||||
|
void entityCameraGetRight(const entityid_t entityId, vec2 out) {
|
||||||
|
componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION);
|
||||||
|
entityposition_t *pos = entityPositionGet(entityId, posComp);
|
||||||
|
// View matrix column layout: right = {M[0][0], M[1][0], M[2][0]}
|
||||||
|
float_t rx = pos->transform[0][0];
|
||||||
|
float_t rz = pos->transform[2][0];
|
||||||
|
float_t len = sqrtf(rx * rx + rz * rz);
|
||||||
|
if(len > 1e-6f) { rx /= len; rz /= len; }
|
||||||
|
out[0] = rx;
|
||||||
|
out[1] = rz;
|
||||||
}
|
}
|
||||||
@@ -55,45 +55,24 @@ void entityCameraGetProjection(
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the near clip distance of a camera.
|
* Returns the entity ID of the first active camera, or ENTITY_COUNT_MAX if none.
|
||||||
*
|
|
||||||
* @param ent The entity ID.
|
|
||||||
* @param comp The component ID.
|
|
||||||
* @return The near clip distance.
|
|
||||||
*/
|
*/
|
||||||
float_t entityCameraGetZNear(const entityid_t ent, const componentid_t comp);
|
entityid_t entityCameraGetCurrent(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the near clip distance of a camera.
|
* Gets the camera's horizontal forward direction (XZ plane) from its position
|
||||||
|
* component. Automatically finds the position component on the entity.
|
||||||
*
|
*
|
||||||
* @param ent The entity ID.
|
* @param entityId The camera entity ID.
|
||||||
* @param comp The component ID.
|
* @param out Output vec2: {forwardX, forwardZ} normalized.
|
||||||
* @param zNear The near clip distance.
|
|
||||||
*/
|
*/
|
||||||
void entityCameraSetZNear(
|
void entityCameraGetForward(const entityid_t entityId, vec2 out);
|
||||||
const entityid_t ent,
|
|
||||||
const componentid_t comp,
|
|
||||||
const float_t zNear
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the far clip distance of a camera.
|
* Gets the camera's horizontal right direction (XZ plane) from its position
|
||||||
|
* component. Automatically finds the position component on the entity.
|
||||||
*
|
*
|
||||||
* @param ent The entity ID.
|
* @param entityId The camera entity ID.
|
||||||
* @param comp The component ID.
|
* @param out Output vec2: {rightX, rightZ} normalized.
|
||||||
* @return The far clip distance.
|
|
||||||
*/
|
*/
|
||||||
float_t entityCameraGetZFar(const entityid_t ent, const componentid_t comp);
|
void entityCameraGetRight(const entityid_t entityId, vec2 out);
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the far clip distance of a camera.
|
|
||||||
*
|
|
||||||
* @param ent The entity ID.
|
|
||||||
* @param comp The component ID.
|
|
||||||
* @param zFar The far clip distance.
|
|
||||||
*/
|
|
||||||
void entityCameraSetZFar(
|
|
||||||
const entityid_t ent,
|
|
||||||
const componentid_t comp,
|
|
||||||
const float_t zFar
|
|
||||||
);
|
|
||||||
@@ -48,4 +48,15 @@ void entityMaterialSetShader(
|
|||||||
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||||
);
|
);
|
||||||
mat->shader = shader;
|
mat->shader = shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
void entityMaterialSetColor(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const color_t color
|
||||||
|
) {
|
||||||
|
entitymaterial_t *mat = componentGetData(
|
||||||
|
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||||
|
);
|
||||||
|
mat->material.unlit.color = color;
|
||||||
}
|
}
|
||||||
@@ -60,4 +60,17 @@ void entityMaterialSetShader(
|
|||||||
const entityid_t entityId,
|
const entityid_t entityId,
|
||||||
const componentid_t componentId,
|
const componentid_t componentId,
|
||||||
shader_t *shader
|
shader_t *shader
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the unlit color for the given entity and component.
|
||||||
|
*
|
||||||
|
* @param entityId The entity ID.
|
||||||
|
* @param componentId The component ID.
|
||||||
|
* @param color The color to set.
|
||||||
|
*/
|
||||||
|
void entityMaterialSetColor(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const color_t color
|
||||||
);
|
);
|
||||||
@@ -5,7 +5,13 @@
|
|||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "entitymesh.h"
|
||||||
#include "entity/entitymanager.h"
|
#include "entity/entitymanager.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "display/mesh/cube.h"
|
||||||
|
#include "display/mesh/plane.h"
|
||||||
|
#include "display/mesh/capsule.h"
|
||||||
|
|
||||||
void entityMeshInit(
|
void entityMeshInit(
|
||||||
const entityid_t entityId,
|
const entityid_t entityId,
|
||||||
@@ -14,7 +20,9 @@ void entityMeshInit(
|
|||||||
entitymesh_t *comp = componentGetData(
|
entitymesh_t *comp = componentGetData(
|
||||||
entityId, componentId, COMPONENT_TYPE_MESH
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
);
|
);
|
||||||
comp->mesh = NULL;
|
comp->mesh = &CUBE_MESH_SIMPLE;
|
||||||
|
comp->ownedVertices = NULL;
|
||||||
|
comp->ownsData = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh_t * entityMeshGetMesh(
|
mesh_t * entityMeshGetMesh(
|
||||||
@@ -36,4 +44,100 @@ void entityMeshSetMesh(
|
|||||||
entityId, componentId, COMPONENT_TYPE_MESH
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
);
|
);
|
||||||
comp->mesh = mesh;
|
comp->mesh = mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void entityMeshDispose(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId
|
||||||
|
) {
|
||||||
|
entitymesh_t *comp = componentGetData(
|
||||||
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
|
);
|
||||||
|
if(!comp->ownsData) return;
|
||||||
|
(void)meshDispose(&comp->ownedMesh);
|
||||||
|
memoryFree(comp->ownedVertices);
|
||||||
|
comp->ownedVertices = NULL;
|
||||||
|
comp->ownsData = false;
|
||||||
|
comp->mesh = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t entityMeshGeneratePlane(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
) {
|
||||||
|
entitymesh_t *comp = componentGetData(
|
||||||
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
|
);
|
||||||
|
entityMeshDispose(entityId, componentId);
|
||||||
|
|
||||||
|
comp->ownedVertices = memoryAllocate(PLANE_VERTEX_COUNT * sizeof(meshvertex_t));
|
||||||
|
assertNotNull(comp->ownedVertices, "Failed to allocate plane vertices");
|
||||||
|
|
||||||
|
vec3 min = { -width * 0.5f, 0.0f, -height * 0.5f };
|
||||||
|
vec3 max = { width * 0.5f, 0.0f, height * 0.5f };
|
||||||
|
vec2 uvMin = { 0.0f, 0.0f };
|
||||||
|
vec2 uvMax = { 1.0f, 1.0f };
|
||||||
|
planeBuffer(
|
||||||
|
comp->ownedVertices,
|
||||||
|
PLANE_AXIS_XZ,
|
||||||
|
min,
|
||||||
|
max
|
||||||
|
#if MESH_ENABLE_COLOR
|
||||||
|
, COLOR_WHITE_4B
|
||||||
|
#endif
|
||||||
|
, uvMin,
|
||||||
|
uvMax
|
||||||
|
);
|
||||||
|
|
||||||
|
errorChain(meshInit(
|
||||||
|
&comp->ownedMesh,
|
||||||
|
PLANE_PRIMITIVE_TYPE,
|
||||||
|
PLANE_VERTEX_COUNT,
|
||||||
|
comp->ownedVertices
|
||||||
|
));
|
||||||
|
|
||||||
|
comp->mesh = &comp->ownedMesh;
|
||||||
|
comp->ownsData = true;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t entityMeshGenerateCapsule(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const float_t radius,
|
||||||
|
const float_t halfHeight
|
||||||
|
) {
|
||||||
|
entitymesh_t *comp = componentGetData(
|
||||||
|
entityId, componentId, COMPONENT_TYPE_MESH
|
||||||
|
);
|
||||||
|
entityMeshDispose(entityId, componentId);
|
||||||
|
|
||||||
|
comp->ownedVertices = memoryAllocate(CAPSULE_VERTEX_COUNT * sizeof(meshvertex_t));
|
||||||
|
assertNotNull(comp->ownedVertices, "Failed to allocate capsule vertices");
|
||||||
|
|
||||||
|
vec3 center = { 0.0f, 0.0f, 0.0f };
|
||||||
|
capsuleBuffer(
|
||||||
|
comp->ownedVertices,
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
halfHeight,
|
||||||
|
CAPSULE_CAP_RINGS,
|
||||||
|
CAPSULE_SECTORS
|
||||||
|
#if MESH_ENABLE_COLOR
|
||||||
|
, COLOR_WHITE_4B
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
|
errorChain(meshInit(
|
||||||
|
&comp->ownedMesh,
|
||||||
|
CAPSULE_PRIMITIVE_TYPE,
|
||||||
|
CAPSULE_VERTEX_COUNT,
|
||||||
|
comp->ownedVertices
|
||||||
|
));
|
||||||
|
|
||||||
|
comp->mesh = &comp->ownedMesh;
|
||||||
|
comp->ownsData = true;
|
||||||
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,9 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
mesh_t *mesh;
|
mesh_t *mesh;
|
||||||
|
mesh_t ownedMesh;
|
||||||
|
meshvertex_t *ownedVertices;
|
||||||
|
bool_t ownsData;
|
||||||
} entitymesh_t;
|
} entitymesh_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,4 +50,47 @@ void entityMeshSetMesh(
|
|||||||
const entityid_t entityId,
|
const entityid_t entityId,
|
||||||
const componentid_t componentId,
|
const componentid_t componentId,
|
||||||
mesh_t *mesh
|
mesh_t *mesh
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes the entity mesh component, freeing any owned mesh data.
|
||||||
|
*
|
||||||
|
* @param entityId The entity ID.
|
||||||
|
* @param componentId The component ID.
|
||||||
|
*/
|
||||||
|
void entityMeshDispose(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an XZ-aligned plane mesh owned by the component.
|
||||||
|
*
|
||||||
|
* @param entityId The entity ID.
|
||||||
|
* @param componentId The component ID.
|
||||||
|
* @param width Width of the plane along the X axis.
|
||||||
|
* @param height Height of the plane along the Z axis.
|
||||||
|
* @return Error return value.
|
||||||
|
*/
|
||||||
|
errorret_t entityMeshGeneratePlane(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const float_t width,
|
||||||
|
const float_t height
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a Y-axis capsule mesh owned by the component.
|
||||||
|
*
|
||||||
|
* @param entityId The entity ID.
|
||||||
|
* @param componentId The component ID.
|
||||||
|
* @param radius Radius of the cylinder and hemisphere caps.
|
||||||
|
* @param halfHeight Half-height of the cylindrical section.
|
||||||
|
* @return Error return value.
|
||||||
|
*/
|
||||||
|
errorret_t entityMeshGenerateCapsule(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const float_t radius,
|
||||||
|
const float_t halfHeight
|
||||||
);
|
);
|
||||||
@@ -100,6 +100,25 @@ bool_t entityPhysicsIsOnGround(
|
|||||||
return phys->onGround;
|
return phys->onGround;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void entityPhysicsSetBodyType(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const physicsbodytype_t type
|
||||||
|
) {
|
||||||
|
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
|
||||||
|
assertNotNull(phys, "Failed to get physics component data");
|
||||||
|
phys->type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
physicsbodytype_t entityPhysicsGetBodyType(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId
|
||||||
|
) {
|
||||||
|
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
|
||||||
|
assertNotNull(phys, "Failed to get physics component data");
|
||||||
|
return phys->type;
|
||||||
|
}
|
||||||
|
|
||||||
void entityPhysicsDispose(
|
void entityPhysicsDispose(
|
||||||
const entityid_t entityId,
|
const entityid_t entityId,
|
||||||
const componentid_t componentId
|
const componentid_t componentId
|
||||||
|
|||||||
@@ -122,6 +122,31 @@ bool_t entityPhysicsIsOnGround(
|
|||||||
const componentid_t componentId
|
const componentid_t componentId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the body type of the entity's physics body.
|
||||||
|
*
|
||||||
|
* @param entityId The entity ID.
|
||||||
|
* @param componentId The component ID.
|
||||||
|
* @param type The body type to set.
|
||||||
|
*/
|
||||||
|
void entityPhysicsSetBodyType(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const physicsbodytype_t type
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the body type of the entity's physics body.
|
||||||
|
*
|
||||||
|
* @param entityId The entity ID.
|
||||||
|
* @param componentId The component ID.
|
||||||
|
* @return The body type of the physics body.
|
||||||
|
*/
|
||||||
|
physicsbodytype_t entityPhysicsGetBodyType(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases the body slot back to PHYSICS_WORLD. Called automatically when
|
* Releases the body slot back to PHYSICS_WORLD. Called automatically when
|
||||||
* the component is disposed via the component system.
|
* the component is disposed via the component system.
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -11,8 +11,14 @@
|
|||||||
#include "entity/component/display/entitymaterial.h"
|
#include "entity/component/display/entitymaterial.h"
|
||||||
#include "entity/component/physics/entityphysics.h"
|
#include "entity/component/physics/entityphysics.h"
|
||||||
|
|
||||||
|
// Name (Uppercase)
|
||||||
|
// Structure
|
||||||
|
// Field name (lowercase)
|
||||||
|
// Init function (optional)
|
||||||
|
// Dispose function (optional)
|
||||||
|
|
||||||
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
||||||
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
||||||
X(MESH, entitymesh_t, mesh, entityMeshInit, NULL)
|
X(MESH, entitymesh_t, mesh, entityMeshInit, entityMeshDispose)
|
||||||
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
|
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
|
||||||
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
|
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "dusk.h"
|
||||||
|
|
||||||
#define ENTITY_COUNT_MAX 6
|
#define ENTITY_COUNT_MAX 20
|
||||||
#define ENTITY_COMPONENT_COUNT_MAX 6
|
#define ENTITY_COMPONENT_COUNT_MAX 8
|
||||||
|
|
||||||
typedef uint8_t entityid_t;
|
typedef uint8_t entityid_t;
|
||||||
typedef uint8_t componentid_t;
|
typedef uint8_t componentid_t;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "entitymanager.h"
|
#include "entitymanager.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "scene/scene.h"
|
#include "console/console.h"
|
||||||
|
|
||||||
entitymanager_t ENTITY_MANAGER;
|
entitymanager_t ENTITY_MANAGER;
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ void entityManagerInit(void) {
|
|||||||
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
|
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
|
||||||
);
|
);
|
||||||
|
|
||||||
sceneLog(
|
consolePrint(
|
||||||
"Entity Manager size: %zu bytes (%.2f KB)\n",
|
"Entity Manager size: %zu bytes (%.2f KB)",
|
||||||
sizeof(entitymanager_t),
|
sizeof(entitymanager_t),
|
||||||
sizeof(entitymanager_t) / 1024.0f
|
sizeof(entitymanager_t) / 1024.0f
|
||||||
);
|
);
|
||||||
|
|||||||
+76
-89
@@ -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
|
||||||
*/
|
*/
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
void eventInit(
|
void eventInit(
|
||||||
event_t *event,
|
event_t *event,
|
||||||
@@ -46,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;
|
||||||
@@ -73,7 +71,7 @@ eventsub_t eventSubscribe(
|
|||||||
const eventcallback_t callback,
|
const eventcallback_t callback,
|
||||||
const void *user
|
const void *user
|
||||||
) {
|
) {
|
||||||
eventSubscribeUser(
|
return eventSubscribeUser(
|
||||||
event,
|
event,
|
||||||
EVENT_TYPE_C,
|
EVENT_TYPE_C,
|
||||||
(eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } }
|
(eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } }
|
||||||
@@ -82,54 +80,34 @@ eventsub_t eventSubscribe(
|
|||||||
|
|
||||||
eventsub_t eventSubscribeScriptContext(
|
eventsub_t eventSubscribeScriptContext(
|
||||||
event_t *event,
|
event_t *event,
|
||||||
scriptcontext_t *context,
|
scriptmanager_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;
|
} while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS);
|
||||||
|
|
||||||
alreadySubbed = true;
|
|
||||||
break;
|
|
||||||
} 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;
|
} while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS);
|
||||||
break;
|
|
||||||
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
|
|
||||||
|
|
||||||
assertTrue(
|
assertTrue(
|
||||||
i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS,
|
i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS,
|
||||||
"Script context has reached maximum event subscriptions"
|
"Script context has reached maximum event subscriptions"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -137,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
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,38 +130,34 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) {
|
|||||||
|
|
||||||
if(event->listenerCount == 0) return;
|
if(event->listenerCount == 0) return;
|
||||||
|
|
||||||
// Find listener
|
|
||||||
uint16_t index = 0;
|
uint16_t index = 0;
|
||||||
do {
|
do {
|
||||||
if(event->listenerArray[index].id == id) {
|
if(event->listenerArray[index].id != id) {
|
||||||
// Found it, remove by swapping with last and reducing count
|
index++;
|
||||||
event->listenerArray[index] = event->listenerArray[--event->listenerCount];
|
continue;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
index++;
|
|
||||||
|
if(event->listenerArray[index].type == EVENT_TYPE_SCRIPT) {
|
||||||
|
jerry_value_t funcVal = event->listenerArray[index].user.script.funcValue;
|
||||||
|
if(funcVal != 0) {
|
||||||
|
jerry_value_free(funcVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event->listenerArray[index] = event->listenerArray[--event->listenerCount];
|
||||||
|
return;
|
||||||
} while(index < event->listenerCount);
|
} while(index < event->listenerCount);
|
||||||
|
|
||||||
// Did we find it?
|
|
||||||
if(index == event->listenerCount) return;
|
|
||||||
|
|
||||||
// Shift remaining listeners down (if any)
|
|
||||||
if(index < event->listenerCount - 1) {
|
|
||||||
memoryMove(
|
|
||||||
&event->listenerArray[index],
|
|
||||||
&event->listenerArray[index + 1],
|
|
||||||
sizeof(eventlistener_t) * (event->listenerCount - index - 1)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
event->listenerCount--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
|
void eventUnsubscribeScriptContext(
|
||||||
|
event_t *event,
|
||||||
|
const scriptmanager_t *ctx
|
||||||
|
) {
|
||||||
assertNotNull(event, "Event cannot be NULL");
|
assertNotNull(event, "Event cannot be NULL");
|
||||||
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];
|
||||||
@@ -189,9 +168,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);
|
||||||
}
|
}
|
||||||
@@ -204,44 +180,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) {
|
||||||
// Call Lua function
|
jerry_value_t funcVal = listener->user.script.funcValue;
|
||||||
lua_State *L = listener->user.script.context->luaState;
|
assertNotNull((void *)(uintptr_t)funcVal, "Script function value is NULL");
|
||||||
assertNotNull(L, "Lua state in event listener cannot be NULL");
|
|
||||||
|
|
||||||
// Push function
|
jerry_value_t callArgs[1];
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
|
jerry_length_t argCount = 0;
|
||||||
|
|
||||||
if(eventParams != NULL && metatableName != NULL) {
|
if(eventParams != NULL) {
|
||||||
lua_getmetatable(L, -1);
|
callArgs[0] = jerry_object();
|
||||||
luaL_getmetatable(L, metatableName);
|
jerry_object_set_native_ptr(
|
||||||
assertTrue(
|
callArgs[0], &JS_PTR_NATIVE_INFO, (void *)eventParams
|
||||||
lua_rawequal(L, -1, -2),
|
|
||||||
"Event parameter metatable does not match expected type"
|
|
||||||
);
|
);
|
||||||
|
argCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call function with 1 arg, 0 return values
|
jerry_value_t result = jerry_call(
|
||||||
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
|
funcVal, jerry_undefined(), callArgs, argCount
|
||||||
const char_t *strErr = lua_tostring(L, -1);
|
);
|
||||||
lua_pop(L, 1);
|
|
||||||
// Log error but continue
|
if(argCount > 0) jerry_value_free(callArgs[0]);
|
||||||
printf("Error invoking Lua event listener: %s\n", strErr);
|
|
||||||
|
if(jerry_value_is_exception(result)) {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jerry_value_free(result);
|
||||||
} else {
|
} else {
|
||||||
assertUnreachable("Unknown event listener type");
|
assertUnreachable("Unknown event listener type");
|
||||||
}
|
}
|
||||||
@@ -249,4 +236,4 @@ void eventInvoke(
|
|||||||
} while(i < event->listenerCount);
|
} while(i < event->listenerCount);
|
||||||
|
|
||||||
event->isInvoking = false;
|
event->isInvoking = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ eventsub_t eventSubscribe(
|
|||||||
*/
|
*/
|
||||||
eventsub_t eventSubscribeScriptContext(
|
eventsub_t eventSubscribeScriptContext(
|
||||||
event_t *event,
|
event_t *event,
|
||||||
scriptcontext_t *context,
|
scriptmanager_t *context,
|
||||||
const int functionIndex
|
jerry_value_t funcValue
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +101,10 @@ void eventUnsubscribe(event_t *event, const eventsub_t subscription);
|
|||||||
* @param event The event to unsubscribe from.
|
* @param event The event to unsubscribe from.
|
||||||
* @param context The script context whose listeners should be removed.
|
* @param context The script context whose listeners should be removed.
|
||||||
*/
|
*/
|
||||||
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx);
|
void eventUnsubscribeScriptContext(
|
||||||
|
event_t *event,
|
||||||
|
const scriptmanager_t *ctx
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke an event, calling all subscribed listeners. Optionally provide event
|
* Invoke an event, calling all subscribed listeners. Optionally provide event
|
||||||
|
|||||||
@@ -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 "eventcallback.h"
|
#include "eventcallback.h"
|
||||||
#include "script/scriptcontext.h"
|
#include "script/scriptmanager.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EVENT_TYPE_C = 0,
|
EVENT_TYPE_C = 0,
|
||||||
@@ -15,8 +15,8 @@ typedef enum {
|
|||||||
} eventtype_t;
|
} eventtype_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
scriptcontext_t *context;
|
scriptmanager_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;
|
||||||
|
|||||||
+34
-28
@@ -95,27 +95,27 @@ void inputUpdate(void) {
|
|||||||
if(TIME.dynamicUpdate) return;
|
if(TIME.dynamicUpdate) return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(INPUT.eventPressed.listenerCount > 0) {
|
// if(INPUT.eventPressed.listenerCount > 0) {
|
||||||
action = &INPUT.actions[0];
|
// action = &INPUT.actions[0];
|
||||||
do {
|
// do {
|
||||||
if(inputPressed(action->action)) {
|
// if(inputPressed(action->action)) {
|
||||||
inputevent_t inputEvent = { .action = action->action };
|
// inputevent_t inputEvent = { .action = action->action };
|
||||||
eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt");
|
// eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt");
|
||||||
}
|
// }
|
||||||
action++;
|
// action++;
|
||||||
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
// } while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if(INPUT.eventReleased.listenerCount > 0) {
|
// if(INPUT.eventReleased.listenerCount > 0) {
|
||||||
action = &INPUT.actions[0];
|
// action = &INPUT.actions[0];
|
||||||
do {
|
// do {
|
||||||
if(inputReleased(action->action)) {
|
// if(inputReleased(action->action)) {
|
||||||
inputevent_t inputEvent = { .action = action->action };
|
// inputevent_t inputEvent = { .action = action->action };
|
||||||
eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt");
|
// eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt");
|
||||||
}
|
// }
|
||||||
action++;
|
// action++;
|
||||||
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
// } while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
float_t inputGetCurrentValue(const inputaction_t action) {
|
float_t inputGetCurrentValue(const inputaction_t action) {
|
||||||
@@ -171,6 +171,16 @@ float_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
|
|||||||
return inputGetCurrentValue(pos) - inputGetCurrentValue(neg);
|
return inputGetCurrentValue(pos) - inputGetCurrentValue(neg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void inputAxis2D(
|
||||||
|
const inputaction_t negX, const inputaction_t posX,
|
||||||
|
const inputaction_t negY, const inputaction_t posY,
|
||||||
|
vec2 result
|
||||||
|
) {
|
||||||
|
assertNotNull(result, "Result vector cannot be null");
|
||||||
|
result[0] = inputAxis(negX, posX);
|
||||||
|
result[1] = inputAxis(negY, posY);
|
||||||
|
}
|
||||||
|
|
||||||
void inputBind(const inputbutton_t button, const inputaction_t act) {
|
void inputBind(const inputbutton_t button, const inputaction_t act) {
|
||||||
assertTrue(
|
assertTrue(
|
||||||
act < INPUT_ACTION_COUNT,
|
act < INPUT_ACTION_COUNT,
|
||||||
@@ -179,19 +189,15 @@ void inputBind(const inputbutton_t button, const inputaction_t act) {
|
|||||||
assertTrue(act != INPUT_ACTION_NULL, "Cannot bind to NULL action");
|
assertTrue(act != INPUT_ACTION_NULL, "Cannot bind to NULL action");
|
||||||
|
|
||||||
// Get the button data for this button.
|
// Get the button data for this button.
|
||||||
inputbuttondata_t *data = INPUT_BUTTON_DATA;
|
inputbuttondata_t *data = inputButtonGetData(button);
|
||||||
do {
|
assertNotNull(data, "Input button not found");
|
||||||
if(memoryCompare(&data->button, &button, sizeof(inputbutton_t)) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
data++;
|
|
||||||
} while(data->name != NULL);
|
|
||||||
assertNotNull(data->name, "Input button not found");
|
|
||||||
|
|
||||||
// Bind the action.
|
// Bind the action.
|
||||||
data->action = act;
|
data->action = act;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
float_t inputDeadzone(const float_t rawValue, const float_t deadzone) {
|
float_t inputDeadzone(const float_t rawValue, const float_t deadzone) {
|
||||||
if(rawValue < deadzone) return 0.0f;
|
if(rawValue < deadzone) return 0.0f;
|
||||||
return (rawValue - deadzone) / (1.0f - deadzone);
|
return (rawValue - deadzone) / (1.0f - deadzone);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ LEFT,
|
|||||||
RIGHT,
|
RIGHT,
|
||||||
ACCEPT,
|
ACCEPT,
|
||||||
CANCEL,
|
CANCEL,
|
||||||
RAGEQUIT
|
RAGEQUIT,
|
||||||
|
CONSOLE,
|
||||||
POINTERX,
|
POINTERX,
|
||||||
POINTERY,
|
POINTERY,
|
||||||
|
@@ -120,6 +120,22 @@ bool_t inputReleased(const inputaction_t action);
|
|||||||
*/
|
*/
|
||||||
float_t inputAxis(const inputaction_t neg, const inputaction_t pos);
|
float_t inputAxis(const inputaction_t neg, const inputaction_t pos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the values of a 2D input axis, defined by two pairs of actions (negative
|
||||||
|
* and positive for each axis).
|
||||||
|
*
|
||||||
|
* @param negX The action representing the negative direction of the X axis.
|
||||||
|
* @param posX The action representing the positive direction of the X axis.
|
||||||
|
* @param negY The action representing the negative direction of the Y axis.
|
||||||
|
* @param posY The action representing the positive direction of the Y axis.
|
||||||
|
* @param result A vec2 to store the resulting axis values (-1.0f to 1.0f).
|
||||||
|
*/
|
||||||
|
void inputAxis2D(
|
||||||
|
const inputaction_t negX, const inputaction_t posX,
|
||||||
|
const inputaction_t negY, const inputaction_t posY,
|
||||||
|
vec2 result
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds an input button to an action.
|
* Binds an input button to an action.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -9,14 +9,14 @@
|
|||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
|
||||||
// inputaction_t inputActionGetByName(const char_t *name) {
|
inputaction_t inputActionGetByName(const char_t *name) {
|
||||||
// assertNotNull(name, "name must not be NULL");
|
assertNotNull(name, "name must not be NULL");
|
||||||
|
|
||||||
// for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||||
// if(INPUT_ACTION_IDS[i] == NULL) continue;
|
if(INPUT_ACTION_IDS[i] == NULL) continue;
|
||||||
// if(stringCompareInsensitive(INPUT_ACTION_IDS[i], name) != 0) continue;
|
if(stringCompareInsensitive(INPUT_ACTION_IDS[i], name) != 0) continue;
|
||||||
// return i;
|
return i;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// return INPUT_ACTION_COUNT;
|
return INPUT_ACTION_COUNT;
|
||||||
// }
|
}
|
||||||
@@ -26,4 +26,4 @@ typedef struct {
|
|||||||
* @param name The name of the input action.
|
* @param name The name of the input action.
|
||||||
* @return The input action, or INPUT_ACTION_COUNT if not found.
|
* @return The input action, or INPUT_ACTION_COUNT if not found.
|
||||||
*/
|
*/
|
||||||
// inputaction_t inputActionGetByName(const char_t *name);
|
inputaction_t inputActionGetByName(const char_t *name);
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
|
||||||
inputbutton_t inputButtonGetByName(const char_t *name) {
|
inputbutton_t inputButtonGetByName(const char_t *name) {
|
||||||
assertNotNull(name, "name must not be NULL");
|
assertNotNull(name, "name must not be NULL");
|
||||||
@@ -26,4 +27,16 @@ inputbutton_t inputButtonGetByName(const char_t *name) {
|
|||||||
|
|
||||||
float_t inputButtonGetValue(const inputbutton_t button) {
|
float_t inputButtonGetValue(const inputbutton_t button) {
|
||||||
return inputButtonGetValuePlatform(button);
|
return inputButtonGetValuePlatform(button);
|
||||||
|
}
|
||||||
|
inputbuttondata_t * inputButtonGetData(const inputbutton_t button) {
|
||||||
|
inputbuttondata_t *data = INPUT_BUTTON_DATA;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(memoryCompare(&data->button, &button, sizeof(inputbutton_t)) == 0) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
data++;
|
||||||
|
} while(data->name != NULL);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -96,4 +96,12 @@ inputbutton_t inputButtonGetByName(const char_t *name);
|
|||||||
* @param button The input button.
|
* @param button The input button.
|
||||||
* @return The current value of the input button (0.0f to 1.0f).
|
* @return The current value of the input button (0.0f to 1.0f).
|
||||||
*/
|
*/
|
||||||
float_t inputButtonGetValue(const inputbutton_t button);
|
float_t inputButtonGetValue(const inputbutton_t button);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the button data for a specific input button.
|
||||||
|
*
|
||||||
|
* @param button The input button to get the data for.
|
||||||
|
* @return The button data, or NULL if not found.
|
||||||
|
*/
|
||||||
|
inputbuttondata_t * inputButtonGetData(const inputbutton_t button);
|
||||||
+272
-133
@@ -1,5 +1,5 @@
|
|||||||
// 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
|
||||||
|
|
||||||
@@ -15,56 +15,16 @@
|
|||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
#include "display/text/text.h"
|
#include "display/text/text.h"
|
||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "script/scriptmanager.h"
|
||||||
|
#include "script/module/scene/modulescene.h"
|
||||||
|
|
||||||
scene_t SCENE;
|
scene_t SCENE;
|
||||||
|
|
||||||
char_t SCENE_LOG[SCENE_LOG_SIZE];
|
|
||||||
|
|
||||||
void sceneLog(const char *fmt, ...) {
|
|
||||||
char temp[512];
|
|
||||||
|
|
||||||
// 1. Format input like printf
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
vsnprintf(temp, sizeof(temp), fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
printf("%s", temp);
|
|
||||||
|
|
||||||
// 2. Split into lines
|
|
||||||
char *lines[64];
|
|
||||||
int line_count = 0;
|
|
||||||
|
|
||||||
char *ptr = temp;
|
|
||||||
while (*ptr && line_count < 64) {
|
|
||||||
lines[line_count++] = ptr;
|
|
||||||
|
|
||||||
char *nl = strchr(ptr, '\n');
|
|
||||||
if (!nl) break;
|
|
||||||
|
|
||||||
*nl = '\0';
|
|
||||||
ptr = nl + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Prepend lines in reverse order (so final order is correct)
|
|
||||||
for (int i = 0; i < line_count; i++) {
|
|
||||||
char new_log[SCENE_LOG_SIZE];
|
|
||||||
|
|
||||||
snprintf(new_log, sizeof(new_log), "%s\n%s", lines[i], SCENE_LOG);
|
|
||||||
|
|
||||||
// Copy back safely
|
|
||||||
strncpy(SCENE_LOG, new_log, SCENE_LOG_SIZE - 1);
|
|
||||||
SCENE_LOG[SCENE_LOG_SIZE - 1] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t sceneInit(void) {
|
errorret_t sceneInit(void) {
|
||||||
memoryZero(&SCENE, sizeof(scene_t));
|
memoryZero(&SCENE, sizeof(scene_t));
|
||||||
|
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
|
||||||
memoryZero(SCENE_LOG, sizeof(SCENE_LOG));
|
|
||||||
sceneLog("Init\n");
|
|
||||||
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,32 +34,30 @@ errorret_t sceneUpdate(void) {
|
|||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if(stringCompare(SCENE.sceneNext, SCENE.sceneCurrent) != 0) {
|
||||||
|
errorChain(sceneSetImmediate(SCENE.sceneNext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SCENE.sceneActive) {
|
||||||
|
errorChain(moduleSceneCall("update"));
|
||||||
|
}
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dusktimeepoch_t LAST;
|
||||||
|
|
||||||
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
|
||||||
);
|
);
|
||||||
if(camCount == 0) errorOk();
|
|
||||||
|
|
||||||
// Get meshes
|
|
||||||
entityid_t meshEnts[ENTITY_COUNT_MAX];
|
|
||||||
componentid_t meshComps[ENTITY_COUNT_MAX];
|
|
||||||
entityid_t meshCount = componentGetEntitiesWithComponent(
|
|
||||||
COMPONENT_TYPE_MESH, meshEnts, meshComps
|
|
||||||
);
|
|
||||||
if(meshCount == 0) errorOk();
|
|
||||||
|
|
||||||
// Prep Matricies
|
|
||||||
mat4 view, proj, model;
|
mat4 view, proj, model;
|
||||||
errorChain(shaderBind(&SHADER_UNLIT));
|
|
||||||
|
|
||||||
// For each camera.
|
// 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];
|
||||||
@@ -112,90 +70,271 @@ errorret_t sceneRender(void) {
|
|||||||
entityCameraGetProjection(camEnt, camComp, proj);
|
entityCameraGetProjection(camEnt, camComp, proj);
|
||||||
entityPositionGetTransform(camEnt, camPos, view);
|
entityPositionGetTransform(camEnt, camPos, view);
|
||||||
|
|
||||||
// For each mesh.
|
// For each entity
|
||||||
for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
|
for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) {
|
||||||
entityid_t meshEnt = meshEnts[meshIndex];
|
// Does this entity have a material?
|
||||||
|
componentid_t matComp = entityGetComponent(
|
||||||
componentid_t meshComp = meshComps[meshIndex];
|
entityId, COMPONENT_TYPE_MATERIAL
|
||||||
mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
|
|
||||||
if(mesh == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentid_t meshPos = entityGetComponent(
|
|
||||||
meshEnt, COMPONENT_TYPE_POSITION
|
|
||||||
);
|
);
|
||||||
if(meshPos == 0xFF) {
|
if(matComp != 0xFF) {
|
||||||
logError("Mesh entity without entity position found\n");
|
// Yes, get the mesh
|
||||||
|
componentid_t meshComp = entityGetComponent(
|
||||||
|
entityId, COMPONENT_TYPE_MESH
|
||||||
|
);
|
||||||
|
|
||||||
|
if(meshComp == 0xFF) {
|
||||||
|
logError("Entity with material component without mesh component found\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yes, get the material and shader.
|
||||||
|
shadermaterial_t *material = entityMaterialGetShaderMaterial(
|
||||||
|
entityId, matComp
|
||||||
|
);
|
||||||
|
shader_t *shader = entityMaterialGetShader(entityId, matComp);
|
||||||
|
if(shader == NULL) {
|
||||||
|
logError("Entity with material component without shader found\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the mesh
|
||||||
|
mesh_t *mesh = entityMeshGetMesh(entityId, meshComp);
|
||||||
|
if(mesh == NULL) {
|
||||||
|
logError("Entity with material component without mesh found\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the transform.
|
||||||
|
componentid_t meshPos = entityGetComponent(
|
||||||
|
entityId, COMPONENT_TYPE_POSITION
|
||||||
|
);
|
||||||
|
if(meshPos == 0xFF) {
|
||||||
|
glm_mat4_identity(model);
|
||||||
|
} else {
|
||||||
|
entityPositionGetTransform(entityId, meshPos, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the mesh.
|
||||||
|
errorChain(shaderBind(shader));
|
||||||
|
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj));
|
||||||
|
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
|
||||||
|
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
|
||||||
|
errorChain(shaderSetMaterial(shader, material));
|
||||||
|
errorChain(meshDraw(mesh, 0, -1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentid_t meshMat = entityGetComponent(
|
// No, in future there may be other renderable types.
|
||||||
meshEnt, COMPONENT_TYPE_MATERIAL
|
|
||||||
);
|
|
||||||
if(meshMat == 0xFF) {
|
|
||||||
logError("Mesh entity without material component found\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
shadermaterial_t *material = entityMaterialGetShaderMaterial(
|
|
||||||
meshEnt, meshMat
|
|
||||||
);
|
|
||||||
shader_t *shader = entityMaterialGetShader(meshEnt, meshMat);
|
|
||||||
if(shader == NULL) {
|
|
||||||
logError("Mesh entity with material component without shader found\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityPositionGetTransform(meshEnt, meshPos, model);
|
|
||||||
|
|
||||||
errorChain(shaderBind(shader));
|
|
||||||
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj));
|
|
||||||
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
|
|
||||||
errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
|
|
||||||
errorChain(shaderSetMaterial(shader, material));
|
|
||||||
errorChain(meshDraw(mesh, 0, -1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here is where UI will go
|
errorOk();
|
||||||
glm_ortho(
|
|
||||||
0.0f, SCREEN.width,
|
|
||||||
SCREEN.height, 0.0f,
|
|
||||||
0.1f, 100.0f,
|
|
||||||
proj
|
|
||||||
);
|
|
||||||
glm_lookat(
|
|
||||||
(vec3){ 0.0f, 0.0f, 1.0f },
|
|
||||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
|
||||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
|
||||||
view
|
|
||||||
);
|
|
||||||
glm_mat4_identity(model);
|
|
||||||
|
|
||||||
errorChain(shaderBind(&SHADER_UNLIT));
|
// if(camCount > 0) {
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
// // For each entity
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
// for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) {
|
||||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
|
||||||
// errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_0TEXTURE, &DEFAULT_FONT_TEXTURE));
|
// }
|
||||||
// errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
|
|
||||||
errorChain(textDraw(
|
// entityid_t meshEnts[ENTITY_COUNT_MAX];
|
||||||
32, 32,
|
// componentid_t meshComps[ENTITY_COUNT_MAX];
|
||||||
// "Hello World",
|
// entityid_t meshCount = componentGetEntitiesWithComponent(
|
||||||
SCENE_LOG,
|
// COMPONENT_TYPE_MESH, meshEnts, meshComps
|
||||||
COLOR_WHITE,
|
// );
|
||||||
&DEFAULT_FONT_TILESET,
|
|
||||||
&DEFAULT_FONT_TEXTURE
|
// if(meshCount > 0) {
|
||||||
));
|
// errorChain(shaderBind(&SHADER_UNLIT));
|
||||||
errorChain(spriteBatchFlush());
|
|
||||||
|
// for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
|
||||||
|
// entityid_t camEnt = camEnts[camIndex];
|
||||||
|
// componentid_t camComp = camComps[camIndex];
|
||||||
|
// componentid_t camPos = entityGetComponent(camEnt, COMPONENT_TYPE_POSITION);
|
||||||
|
// if(camPos == 0xFF) {
|
||||||
|
// logError("Camera entity without entity position found\n");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// entityCameraGetProjection(camEnt, camComp, proj);
|
||||||
|
// entityPositionGetTransform(camEnt, camPos, view);
|
||||||
|
|
||||||
|
// for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
|
||||||
|
// entityid_t meshEnt = meshEnts[meshIndex];
|
||||||
|
// componentid_t meshComp = meshComps[meshIndex];
|
||||||
|
// mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
|
||||||
|
// if(mesh == NULL) continue;
|
||||||
|
|
||||||
|
// componentid_t meshPos = entityGetComponent(
|
||||||
|
// meshEnt, COMPONENT_TYPE_POSITION
|
||||||
|
// );
|
||||||
|
// if(meshPos == 0xFF) {
|
||||||
|
// logError("Mesh entity without entity position found\n");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// componentid_t meshMat = entityGetComponent(
|
||||||
|
// meshEnt, COMPONENT_TYPE_MATERIAL
|
||||||
|
// );
|
||||||
|
// if(meshMat == 0xFF) {
|
||||||
|
// logError("Mesh entity without material component found\n");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// shadermaterial_t *material = entityMaterialGetShaderMaterial(
|
||||||
|
// meshEnt, meshMat
|
||||||
|
// );
|
||||||
|
// shader_t *shader = entityMaterialGetShader(meshEnt, meshMat);
|
||||||
|
// if(shader == NULL) {
|
||||||
|
// logError("Mesh entity with material component without shader found\n");
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// entityPositionGetTransform(meshEnt, meshPos, model);
|
||||||
|
|
||||||
|
// errorChain(shaderBind(shader));
|
||||||
|
// errorChain(shaderSetMatrix(shader, SHADER_UNLIT_PROJECTION, proj));
|
||||||
|
// errorChain(shaderSetMatrix(shader, SHADER_UNLIT_VIEW, view));
|
||||||
|
// errorChain(shaderSetMatrix(shader, SHADER_UNLIT_MODEL, model));
|
||||||
|
// errorChain(shaderSetMaterial(shader, material));
|
||||||
|
// errorChain(meshDraw(mesh, 0, -1));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// glm_ortho(
|
||||||
|
// 0.0f, SCREEN.width,
|
||||||
|
// SCREEN.height, 0.0f,
|
||||||
|
// 0.1f, 100.0f,
|
||||||
|
// proj
|
||||||
|
// );
|
||||||
|
// glm_lookat(
|
||||||
|
// (vec3){ 0.0f, 0.0f, 1.0f },
|
||||||
|
// (vec3){ 0.0f, 0.0f, 0.0f },
|
||||||
|
// (vec3){ 0.0f, 1.0f, 0.0f },
|
||||||
|
// view
|
||||||
|
// );
|
||||||
|
// glm_mat4_identity(model);
|
||||||
|
|
||||||
|
// errorChain(shaderBind(&SHADER_UNLIT));
|
||||||
|
// errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
|
||||||
|
// errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
|
||||||
|
// errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
|
||||||
|
|
||||||
|
// {
|
||||||
|
// entityid_t sprEnts[ENTITY_COUNT_MAX];
|
||||||
|
// componentid_t sprComps[ENTITY_COUNT_MAX];
|
||||||
|
// entityid_t sprCount = componentGetEntitiesWithComponent(
|
||||||
|
// COMPONENT_TYPE_SPRITE, sprEnts, sprComps
|
||||||
|
// );
|
||||||
|
// for(entityid_t si = 0; si < sprCount; si++) {
|
||||||
|
// entitysprite_t *spr = entitySpriteGet(sprEnts[si], sprComps[si]);
|
||||||
|
// vec3 pos = { 0.0f, 0.0f, 0.0f };
|
||||||
|
// componentid_t posComp = entityGetComponent(
|
||||||
|
// sprEnts[si], COMPONENT_TYPE_POSITION
|
||||||
|
// );
|
||||||
|
// if(posComp != 0xFF) {
|
||||||
|
// entityPositionGetPosition(sprEnts[si], posComp, pos);
|
||||||
|
// }
|
||||||
|
// errorChain(shaderSetTexture(
|
||||||
|
// &SHADER_UNLIT, SHADER_UNLIT_TEXTURE, spr->texture
|
||||||
|
// ));
|
||||||
|
// #if !MESH_ENABLE_COLOR
|
||||||
|
// errorChain(shaderSetColor(
|
||||||
|
// &SHADER_UNLIT, SHADER_UNLIT_COLOR, spr->color
|
||||||
|
// ));
|
||||||
|
// #endif
|
||||||
|
// errorChain(spriteBatchPush(
|
||||||
|
// pos[0], pos[1],
|
||||||
|
// pos[0] + spr->width, pos[1] + spr->height,
|
||||||
|
// #if MESH_ENABLE_COLOR
|
||||||
|
// spr->color,
|
||||||
|
// #endif
|
||||||
|
// spr->uv[0], spr->uv[1],
|
||||||
|
// spr->uv[2], spr->uv[3]
|
||||||
|
// ));
|
||||||
|
// errorChain(spriteBatchFlush());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// errorChain(consoleDraw());
|
||||||
|
|
||||||
|
// // FPS
|
||||||
|
// char_t fpsText[32];
|
||||||
|
|
||||||
|
// dusktimeepoch_t now = timeGetEpoch();
|
||||||
|
// double_t delta = now.time - LAST.time;
|
||||||
|
// LAST = now;
|
||||||
|
// double_t fps = delta > 0 ? 1.0 / delta : 0.0;
|
||||||
|
// snprintf(fpsText, sizeof(fpsText), "FPS: %.2f", fps);
|
||||||
|
|
||||||
|
// errorChain(spriteBatchFlush());
|
||||||
|
// errorChain(textDraw(
|
||||||
|
// 0, 0,
|
||||||
|
// fpsText, COLOR_WHITE,
|
||||||
|
// &FONT_TILESET_DEFAULT, &FONT_TEXTURE_DEFAULT
|
||||||
|
// ));
|
||||||
|
// errorChain(spriteBatchFlush());
|
||||||
|
|
||||||
|
// errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t sceneSetImmediate(const char_t *scene) {
|
||||||
|
if(scene != SCENE.sceneNext) {
|
||||||
|
stringCopy(
|
||||||
|
SCENE.sceneNext,
|
||||||
|
scene == NULL ? "" : scene,
|
||||||
|
ASSET_FILE_PATH_MAX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SCENE.sceneActive) {
|
||||||
|
errorChain(moduleSceneCall("dispose"));
|
||||||
|
SCENE.sceneActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleSceneReset();
|
||||||
|
|
||||||
|
stringCopy(
|
||||||
|
SCENE.sceneCurrent,
|
||||||
|
scene == NULL ? "" : scene,
|
||||||
|
ASSET_FILE_PATH_MAX
|
||||||
|
);
|
||||||
|
|
||||||
|
if(scene != NULL) {
|
||||||
|
jerry_value_t sceneClass = SCENE_SCRIPT_REF_NONE;
|
||||||
|
errorChain(scriptManagerExecFile(scene, &sceneClass));
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(sceneClass)) {
|
||||||
|
if(sceneClass != SCENE_SCRIPT_REF_NONE) jerry_value_free(sceneClass);
|
||||||
|
errorThrow("Scene '%s' must export a constructor function", scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t sceneObj = jerry_construct(sceneClass, NULL, 0);
|
||||||
|
jerry_value_free(sceneClass);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(sceneObj)) {
|
||||||
|
char_t errMsg[512];
|
||||||
|
moduleBaseExceptionMessage(sceneObj, errMsg, sizeof(errMsg));
|
||||||
|
jerry_value_free(sceneObj);
|
||||||
|
errorThrow("Scene '%s' constructor threw: %s", scene, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENE.scriptRef = sceneObj;
|
||||||
|
SCENE.sceneActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t sceneSet(const char_t *script) {
|
void sceneSet(const char_t *scene) {
|
||||||
errorOk();
|
stringCopy(
|
||||||
|
SCENE.sceneNext,
|
||||||
|
scene == NULL ? "" : scene,
|
||||||
|
ASSET_FILE_PATH_MAX
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sceneDispose(void) {
|
errorret_t sceneDispose(void) {
|
||||||
|
errorChain(moduleSceneCall("dispose"));
|
||||||
}
|
errorOk();
|
||||||
|
}
|
||||||
|
|||||||
+37
-21
@@ -1,52 +1,68 @@
|
|||||||
/**
|
/**
|
||||||
* 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 "error/error.h"
|
#include "script/scriptmanager.h"
|
||||||
|
#include "asset/assetfile.h"
|
||||||
|
#include "event/event.h"
|
||||||
|
|
||||||
|
#define SCENE_EVENT_UPDATE_MAX 16
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void *nothing;
|
bool_t sceneActive;
|
||||||
|
jerry_value_t scriptRef;
|
||||||
|
char_t sceneCurrent[ASSET_FILE_PATH_MAX];
|
||||||
|
char_t sceneNext[ASSET_FILE_PATH_MAX];
|
||||||
} scene_t;
|
} scene_t;
|
||||||
|
|
||||||
extern scene_t SCENE;
|
extern scene_t SCENE;
|
||||||
|
|
||||||
#define SCENE_LOG_SIZE 1024
|
/** Sentinel value meaning no scene script is loaded. */
|
||||||
extern char_t SCENE_LOG[SCENE_LOG_SIZE];
|
#define SCENE_SCRIPT_REF_NONE ((jerry_value_t)0)
|
||||||
void sceneLog(const char *fmt, ...);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the scene subsystem.
|
* Initializes the scene manager.
|
||||||
*
|
*
|
||||||
* @return The error return value.
|
* @return Any error state that happened.
|
||||||
*/
|
*/
|
||||||
errorret_t sceneInit(void);
|
errorret_t sceneInit(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the current scene.
|
* Ticks the scene manager; may call the scene's update method.
|
||||||
*
|
*
|
||||||
* @return The error return value.
|
* @return Any error state that happened.
|
||||||
*/
|
*/
|
||||||
errorret_t sceneUpdate(void);
|
errorret_t sceneUpdate(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the current scene.
|
* Renders the scene.
|
||||||
*
|
*
|
||||||
* @return The error return value.
|
* @return Any error state that happened.
|
||||||
*/
|
*/
|
||||||
errorret_t sceneRender(void);
|
errorret_t sceneRender(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current scene by script name.
|
* Immediately switches scenes, disposing the current one first.
|
||||||
*
|
*
|
||||||
* @param script The script name of the scene to set.
|
* @param scene Scene to switch to (asset file path).
|
||||||
|
* @return Any error state that happened.
|
||||||
*/
|
*/
|
||||||
errorret_t sceneSet(const char_t *script);
|
errorret_t sceneSetImmediate(const char_t *scene);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dispose of the scene subsystem.
|
* Requests a scene change on the next safe opportunity.
|
||||||
|
*
|
||||||
|
* @param scene Which scene to set.
|
||||||
*/
|
*/
|
||||||
void sceneDispose(void);
|
void sceneSet(const char_t *scene);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the current scene.
|
||||||
|
*
|
||||||
|
* @return Any error state that happened.
|
||||||
|
*/
|
||||||
|
errorret_t sceneDispose(void);
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
scriptmanager.c
|
scriptmanager.c
|
||||||
scriptcontext.c
|
scriptproto.c
|
||||||
scriptmodule.c
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Subdirectories
|
# Subdirectories
|
||||||
add_subdirectory(module)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Copyright (c) 2026 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Subdirectories
|
|
||||||
add_subdirectory(display)
|
|
||||||
add_subdirectory(event)
|
|
||||||
add_subdirectory(input)
|
|
||||||
add_subdirectory(locale)
|
|
||||||
add_subdirectory(system)
|
|
||||||
add_subdirectory(scene)
|
|
||||||
add_subdirectory(time)
|
|
||||||
add_subdirectory(ui)
|
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_CONSOLE_PROTO;
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleConsolePrint) {
|
||||||
|
char_t buf[512];
|
||||||
|
char_t msg[4096];
|
||||||
|
size_t msgLen = 0;
|
||||||
|
|
||||||
|
for(jerry_length_t i = 0; i < argc; ++i) {
|
||||||
|
jerry_value_t strVal = jerry_value_to_string(args[i]);
|
||||||
|
moduleBaseToString(strVal, buf, sizeof(buf));
|
||||||
|
jerry_value_free(strVal);
|
||||||
|
|
||||||
|
size_t partLen = strlen(buf);
|
||||||
|
if(msgLen + partLen + 1 < sizeof(msg)) {
|
||||||
|
stringCopy(msg + msgLen, buf, sizeof(msg) - msgLen);
|
||||||
|
msgLen += partLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i + 1 < argc && msgLen + 1 < sizeof(msg)) {
|
||||||
|
msg[msgLen++] = '\t';
|
||||||
|
msg[msgLen] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
consolePrint("%s", msg);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleConsoleGetVisible) {
|
||||||
|
return jerry_boolean(CONSOLE.visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleConsoleSetVisible) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
if(!jerry_value_is_boolean(args[0])) {
|
||||||
|
return moduleBaseThrow("Console.visible: expected boolean");
|
||||||
|
}
|
||||||
|
CONSOLE.visible = jerry_value_is_true(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleConsole(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_CONSOLE_PROTO, "Console",
|
||||||
|
sizeof(uint8_t), NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_CONSOLE_PROTO, "print", moduleConsolePrint
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticProp(
|
||||||
|
&MODULE_CONSOLE_PROTO, "visible",
|
||||||
|
moduleConsoleGetVisible, moduleConsoleSetVisible
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Copyright (c) 2026 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
moduleglm.c
|
|
||||||
modulespritebatch.c
|
|
||||||
moduleglm.c
|
|
||||||
modulecolor.c
|
|
||||||
moduletext.c
|
|
||||||
modulescreen.c
|
|
||||||
moduletileset.c
|
|
||||||
moduletexture.c
|
|
||||||
moduleshader.c
|
|
||||||
)
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "modulecolor.h"
|
|
||||||
#include "display/color.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
#include "time/time.h"
|
|
||||||
|
|
||||||
void moduleColor(scriptcontext_t *context) {
|
|
||||||
assertNotNull(context, "Context cannot be NULL.");
|
|
||||||
|
|
||||||
if(luaL_newmetatable(context->luaState, "color_mt")) {
|
|
||||||
// Metatable is new, set __index and __newindex
|
|
||||||
lua_pushstring(context->luaState, "__index");
|
|
||||||
lua_pushcfunction(context->luaState, moduleColorIndex);
|
|
||||||
lua_settable(context->luaState, -3);
|
|
||||||
|
|
||||||
lua_pushstring(context->luaState, "__newindex");
|
|
||||||
lua_pushcfunction(context->luaState, moduleColorNewIndex);
|
|
||||||
lua_settable(context->luaState, -3);
|
|
||||||
|
|
||||||
lua_pushstring(context->luaState, "__tostring");
|
|
||||||
lua_pushcfunction(context->luaState, moduleColorToString);
|
|
||||||
lua_settable(context->luaState, -3);
|
|
||||||
}
|
|
||||||
lua_pop(context->luaState, 1);
|
|
||||||
|
|
||||||
lua_register(context->luaState, "color", moduleColorFuncColor);
|
|
||||||
lua_register(context->luaState, "colorRainbow", moduleColorRainbow);
|
|
||||||
|
|
||||||
scriptContextExec(context, COLOR_SCRIPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleColorFuncColor(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L);
|
|
||||||
assertNotNull(context, "Script context cannot be NULL.");
|
|
||||||
|
|
||||||
// Needs 4 channel uint8_t
|
|
||||||
if(
|
|
||||||
!lua_isnumber(L, 1) || !lua_isnumber(L, 2) ||
|
|
||||||
!lua_isnumber(L, 3) || !lua_isnumber(L, 4)
|
|
||||||
) {
|
|
||||||
return luaL_error(L, "color(r, g, b, a) requires four number arguments.");
|
|
||||||
}
|
|
||||||
|
|
||||||
colorchannel8_t r = (colorchannel8_t)lua_tonumber(L, 1);
|
|
||||||
colorchannel8_t g = (colorchannel8_t)lua_tonumber(L, 2);
|
|
||||||
colorchannel8_t b = (colorchannel8_t)lua_tonumber(L, 3);
|
|
||||||
colorchannel8_t a = (colorchannel8_t)lua_tonumber(L, 4);
|
|
||||||
|
|
||||||
// Create color_t as lua memory, and push metatable
|
|
||||||
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
|
|
||||||
luaL_getmetatable(L, "color_mt");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
// Initial values.
|
|
||||||
color->r = r;
|
|
||||||
color->g = g;
|
|
||||||
color->b = b;
|
|
||||||
color->a = a;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleColorIndex(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *key = lua_tostring(L, 2);
|
|
||||||
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
||||||
|
|
||||||
const color_t *color = (const color_t*)luaL_checkudata(L, 1, "color_mt");
|
|
||||||
assertNotNull(color, "Color struct cannot be NULL.");
|
|
||||||
|
|
||||||
if(stringCompare(key, "r") == 0) {
|
|
||||||
lua_pushnumber(L, color->r);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if(stringCompare(key, "g") == 0) {
|
|
||||||
lua_pushnumber(L, color->g);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if(stringCompare(key, "b") == 0) {
|
|
||||||
lua_pushnumber(L, color->b);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if(stringCompare(key, "a") == 0) {
|
|
||||||
lua_pushnumber(L, color->a);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushnil(L);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleColorNewIndex(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *key = lua_tostring(L, 2);
|
|
||||||
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
||||||
|
|
||||||
color_t *color = (color_t*)luaL_checkudata(L, 1, "color_mt");
|
|
||||||
assertNotNull(color, "Color struct cannot be NULL.");
|
|
||||||
|
|
||||||
if(stringCompare(key, "r") == 0) {
|
|
||||||
if(!lua_isnumber(L, 3)) {
|
|
||||||
return luaL_error(L, "color channel r must be a number.");
|
|
||||||
}
|
|
||||||
color->r = (colorchannel8_t)lua_tonumber(L, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "g") == 0) {
|
|
||||||
if(!lua_isnumber(L, 3)) {
|
|
||||||
return luaL_error(L, "color channel g must be a number.");
|
|
||||||
}
|
|
||||||
color->g = (colorchannel8_t)lua_tonumber(L, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "b") == 0) {
|
|
||||||
if(!lua_isnumber(L, 3)) {
|
|
||||||
return luaL_error(L, "color channel b must be a number.");
|
|
||||||
}
|
|
||||||
color->b = (colorchannel8_t)lua_tonumber(L, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "a") == 0) {
|
|
||||||
if(!lua_isnumber(L, 3)) {
|
|
||||||
return luaL_error(L, "color channel a must be a number.");
|
|
||||||
}
|
|
||||||
color->a = (colorchannel8_t)lua_tonumber(L, 3);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleColorToString(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleColorRainbow(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
// Allow time offset
|
|
||||||
float_t t = TIME.time;
|
|
||||||
if(lua_gettop(L) >= 1) {
|
|
||||||
if(!lua_isnumber(L, 1)) {
|
|
||||||
return luaL_error(L, "Rainbow time offset must be a number.");
|
|
||||||
}
|
|
||||||
t += (float_t)lua_tonumber(L, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow speed multiplier
|
|
||||||
if(lua_gettop(L) >= 2) {
|
|
||||||
if(!lua_isnumber(L, 2)) {
|
|
||||||
return luaL_error(L, "Rainbow speed multiplier must be a number.");
|
|
||||||
}
|
|
||||||
t *= (float_t)lua_tonumber(L, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate rainbow based on time.
|
|
||||||
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
|
|
||||||
color->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);
|
|
||||||
color->b = (colorchannel8_t)((sinf(t + 4.0f) + 1.0f) * 0.5f * 255.0f);
|
|
||||||
color->a = 255;
|
|
||||||
|
|
||||||
// Set metatable
|
|
||||||
luaL_getmetatable(L, "color_mt");
|
|
||||||
lua_setmetatable(L, -2);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,55 +1,158 @@
|
|||||||
/**
|
/**
|
||||||
* 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"
|
||||||
|
#include "display/color.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
|
||||||
/**
|
static scriptproto_t MODULE_COLOR_PROTO;
|
||||||
* Registers the color module with the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register the module with.
|
|
||||||
*/
|
|
||||||
void moduleColor(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
static inline color_t * moduleColorGet(
|
||||||
* Lua function to create a color.
|
const jerry_call_info_t *callInfo
|
||||||
*
|
) {
|
||||||
* @param L The Lua state.
|
return (color_t*)scriptProtoGetValue(
|
||||||
* @return Number of return values.
|
&MODULE_COLOR_PROTO, callInfo->this_value
|
||||||
*/
|
);
|
||||||
int moduleColorFuncColor(lua_State *L);
|
}
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleColorGetR) {
|
||||||
* Index function for the color structure.
|
color_t *c = moduleColorGet(callInfo);
|
||||||
* @param L The Lua state.
|
return c ? jerry_number(c->r) : jerry_undefined();
|
||||||
* @return Number of return values.
|
}
|
||||||
*/
|
|
||||||
int moduleColorIndex(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleColorGetG) {
|
||||||
* New index function for the color structure.
|
color_t *c = moduleColorGet(callInfo);
|
||||||
*
|
return c ? jerry_number(c->g) : jerry_undefined();
|
||||||
* @param L The Lua state.
|
}
|
||||||
* @return Number of return values.
|
|
||||||
*/
|
|
||||||
int moduleColorNewIndex(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleColorGetB) {
|
||||||
* Color to string method for script
|
color_t *c = moduleColorGet(callInfo);
|
||||||
*
|
return c ? jerry_number(c->b) : jerry_undefined();
|
||||||
* @param L The Lua state.
|
}
|
||||||
* @return Number of return values.
|
|
||||||
*/
|
|
||||||
int moduleColorToString(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleColorGetA) {
|
||||||
* Lua function to create a rainbow color based on time.
|
color_t *c = moduleColorGet(callInfo);
|
||||||
*
|
return c ? jerry_number(c->a) : jerry_undefined();
|
||||||
* @param L The Lua state.
|
}
|
||||||
* @return Number of return values.
|
|
||||||
*/
|
moduleBaseFunction(moduleColorSetR) {
|
||||||
int moduleColorRainbow(lua_State *L);
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
color_t *c = moduleColorGet(callInfo);
|
||||||
|
if(!c) return jerry_undefined();
|
||||||
|
c->r = (colorchannel8_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleColorSetG) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
color_t *c = moduleColorGet(callInfo);
|
||||||
|
if(!c) return jerry_undefined();
|
||||||
|
c->g = (colorchannel8_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleColorSetB) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
color_t *c = moduleColorGet(callInfo);
|
||||||
|
if(!c) return jerry_undefined();
|
||||||
|
c->b = (colorchannel8_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleColorSetA) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
color_t *c = moduleColorGet(callInfo);
|
||||||
|
if(!c) return jerry_undefined();
|
||||||
|
c->a = (colorchannel8_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleColorToString) {
|
||||||
|
color_t *c = moduleColorGet(callInfo);
|
||||||
|
if(!c) return jerry_undefined();
|
||||||
|
char_t buf[64];
|
||||||
|
stringFormat(
|
||||||
|
buf, sizeof(buf),
|
||||||
|
"{ \"r\": %d, \"g\": %d, \"b\": %d, \"a\": %d }",
|
||||||
|
(int32_t)c->r, (int32_t)c->g,
|
||||||
|
(int32_t)c->b, (int32_t)c->a
|
||||||
|
);
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static jerry_value_t moduleColorMakeObject(color_t color) {
|
||||||
|
return scriptProtoCreateValue(&MODULE_COLOR_PROTO, &color);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleColorConstructor) {
|
||||||
|
if(argc > 0 && !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Color: r must be a number");
|
||||||
|
}
|
||||||
|
if(argc > 1 && !jerry_value_is_number(args[1])) {
|
||||||
|
return moduleBaseThrow("Color: g must be a number");
|
||||||
|
}
|
||||||
|
if(argc > 2 && !jerry_value_is_number(args[2])) {
|
||||||
|
return moduleBaseThrow("Color: b must be a number");
|
||||||
|
}
|
||||||
|
if(argc > 3 && !jerry_value_is_number(args[3])) {
|
||||||
|
return moduleBaseThrow("Color: a must be a number");
|
||||||
|
}
|
||||||
|
color_t c;
|
||||||
|
c.r = argc > 0 ? (colorchannel8_t)jerry_value_as_number(args[0]) : 255;
|
||||||
|
c.g = argc > 1 ? (colorchannel8_t)jerry_value_as_number(args[1]) : 255;
|
||||||
|
c.b = argc > 2 ? (colorchannel8_t)jerry_value_as_number(args[2]) : 255;
|
||||||
|
c.a = argc > 3 ? (colorchannel8_t)jerry_value_as_number(args[3]) : 255;
|
||||||
|
return moduleColorMakeObject(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleColorRainbow) {
|
||||||
|
float_t t;
|
||||||
|
|
||||||
|
if(argc >= 1 && jerry_value_is_number(args[0])) {
|
||||||
|
t = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
} else {
|
||||||
|
t = TIME.time * 4.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argc >= 2 && jerry_value_is_number(args[1])) {
|
||||||
|
t *= (float_t)jerry_value_as_number(args[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
color_t c;
|
||||||
|
c.r = (colorchannel8_t)((sinf(t) + 1.0f) * 0.5f * 255.0f);
|
||||||
|
c.g = (colorchannel8_t)((sinf(t + 2.0f) + 1.0f) * 0.5f * 255.0f);
|
||||||
|
c.b = (colorchannel8_t)((sinf(t + 4.0f) + 1.0f) * 0.5f * 255.0f);
|
||||||
|
c.a = 255;
|
||||||
|
return moduleColorMakeObject(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleColor(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_COLOR_PROTO, "Color", sizeof(color_t), moduleColorConstructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_COLOR_PROTO, "r", moduleColorGetR, moduleColorSetR
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_COLOR_PROTO, "g", moduleColorGetG, moduleColorSetG
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_COLOR_PROTO, "b", moduleColorGetB, moduleColorSetB
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_COLOR_PROTO, "a", moduleColorGetA, moduleColorSetA
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_COLOR_PROTO, "rainbow", moduleColorRainbow
|
||||||
|
);
|
||||||
|
scriptProtoDefineToString(&MODULE_COLOR_PROTO, moduleColorToString);
|
||||||
|
moduleBaseEval(COLOR_SCRIPT);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,283 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "moduleglm.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
|
|
||||||
void moduleGLM(scriptcontext_t *context) {
|
|
||||||
assertNotNull(context, "Context cannot be NULL.");
|
|
||||||
|
|
||||||
// Create metatable for vec3 structure.
|
|
||||||
if(luaL_newmetatable(context->luaState, "vec3_mt")) {
|
|
||||||
// Metatable methods
|
|
||||||
lua_pushcfunction(context->luaState, moduleVec3Index);
|
|
||||||
lua_setfield(context->luaState, -2, "__index");
|
|
||||||
lua_pushcfunction(context->luaState, moduleVec3NewIndex);
|
|
||||||
lua_setfield(context->luaState, -2, "__newindex");
|
|
||||||
lua_pushcfunction(context->luaState, moduleVec3ToString);
|
|
||||||
lua_setfield(context->luaState, -2, "__tostring");
|
|
||||||
}
|
|
||||||
lua_pop(context->luaState, 1);
|
|
||||||
|
|
||||||
if(luaL_newmetatable(context->luaState, "vec4_mt")) {
|
|
||||||
// Metatable methods
|
|
||||||
lua_pushcfunction(context->luaState, moduleVec4Index);
|
|
||||||
lua_setfield(context->luaState, -2, "__index");
|
|
||||||
lua_pushcfunction(context->luaState, moduleVec4NewIndex);
|
|
||||||
lua_setfield(context->luaState, -2, "__newindex");
|
|
||||||
lua_pushcfunction(context->luaState, moduleVec4ToString);
|
|
||||||
lua_setfield(context->luaState, -2, "__tostring");
|
|
||||||
}
|
|
||||||
lua_pop(context->luaState, 1);
|
|
||||||
|
|
||||||
lua_register(context->luaState, "vec3", moduleVec3Create);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int moduleVec3Create(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
|
|
||||||
memoryZero(v, sizeof(vec3));
|
|
||||||
|
|
||||||
// May be expecting between 1 and 3 values.
|
|
||||||
int top = lua_gettop(l);
|
|
||||||
if(top >= 1) {
|
|
||||||
if(!lua_isnumber(l, 1)) {
|
|
||||||
luaL_error(l, "Vec3 x component must be a number.");
|
|
||||||
}
|
|
||||||
(*v)[0] = (float_t)lua_tonumber(l, 1);
|
|
||||||
}
|
|
||||||
if(top >= 2) {
|
|
||||||
if(!lua_isnumber(l, 2)) {
|
|
||||||
luaL_error(l, "Vec3 y component must be a number.");
|
|
||||||
}
|
|
||||||
(*v)[1] = (float_t)lua_tonumber(l, 2);
|
|
||||||
}
|
|
||||||
if(top >= 3) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec3 z component must be a number.");
|
|
||||||
}
|
|
||||||
(*v)[2] = (float_t)lua_tonumber(l, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set metatable
|
|
||||||
luaL_getmetatable(l, "vec3_mt");
|
|
||||||
lua_setmetatable(l, -2);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleVec3Index(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *key = lua_tostring(l, 2);
|
|
||||||
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
||||||
|
|
||||||
vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt");
|
|
||||||
assertNotNull(vec, "Vec3 pointer cannot be NULL.");
|
|
||||||
|
|
||||||
if(stringCompare(key, "x") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[0]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "y") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[1]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "z") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[2]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleVec3NewIndex(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *key = luaL_checkstring(l, 2);
|
|
||||||
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
||||||
|
|
||||||
vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt");
|
|
||||||
assertNotNull(vec, "Vec3 pointer cannot be NULL.");
|
|
||||||
|
|
||||||
if(stringCompare(key, "x") == 0) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec3 x component must be a number.");
|
|
||||||
}
|
|
||||||
(*vec)[0] = (float_t)lua_tonumber(l, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "y") == 0) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec3 y component must be a number.");
|
|
||||||
}
|
|
||||||
(*vec)[1] = (float_t)lua_tonumber(l, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "z") == 0) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec3 z component must be a number.");
|
|
||||||
}
|
|
||||||
(*vec)[2] = (float_t)lua_tonumber(l, 3);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
luaL_error(l, "Invalid key for vec3: %s", key);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleVec3ToString(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
vec3 *vec = (vec3 *)luaL_checkudata(l, 1, "vec3_mt");
|
|
||||||
assertNotNull(vec, "Vec3 pointer cannot be NULL.");
|
|
||||||
|
|
||||||
char buf[128];
|
|
||||||
snprintf(
|
|
||||||
buf, sizeof(buf),
|
|
||||||
"vec3(%.3f, %.3f, %.3f)",
|
|
||||||
(*vec)[0], (*vec)[1], (*vec)[2]
|
|
||||||
);
|
|
||||||
lua_pushstring(l, buf);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int moduleVec4Create(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
vec4 *v = (vec4 *)lua_newuserdata(l, sizeof(vec4));
|
|
||||||
memoryZero(v, sizeof(vec4));
|
|
||||||
|
|
||||||
// May be expecting between 1 and 4 values.
|
|
||||||
int top = lua_gettop(l);
|
|
||||||
if(top >= 1) {
|
|
||||||
if(!lua_isnumber(l, 1)) {
|
|
||||||
luaL_error(l, "Vec4 x component must be a number.");
|
|
||||||
}
|
|
||||||
(*v)[0] = (float_t)lua_tonumber(l, 1);
|
|
||||||
}
|
|
||||||
if(top >= 2) {
|
|
||||||
if(!lua_isnumber(l, 2)) {
|
|
||||||
luaL_error(l, "Vec4 y component must be a number.");
|
|
||||||
}
|
|
||||||
(*v)[1] = (float_t)lua_tonumber(l, 2);
|
|
||||||
}
|
|
||||||
if(top >= 3) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec4 z component must be a number.");
|
|
||||||
}
|
|
||||||
(*v)[2] = (float_t)lua_tonumber(l, 3);
|
|
||||||
}
|
|
||||||
if(top >= 4) {
|
|
||||||
if(!lua_isnumber(l, 4)) {
|
|
||||||
luaL_error(l, "Vec4 w component must be a number.");
|
|
||||||
}
|
|
||||||
(*v)[3] = (float_t)lua_tonumber(l, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set metatable
|
|
||||||
luaL_getmetatable(l, "vec4_mt");
|
|
||||||
lua_setmetatable(l, -2);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleVec4Index(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *key = lua_tostring(l, 2);
|
|
||||||
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
||||||
|
|
||||||
vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt");
|
|
||||||
assertNotNull(vec, "Vec4 pointer cannot be NULL.");
|
|
||||||
|
|
||||||
if(stringCompare(key, "x") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[0]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "y") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[1]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "z") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[2]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "w") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[3]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "u0") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[0]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "v0") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[1]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "u1") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[2]);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "v1") == 0) {
|
|
||||||
lua_pushnumber(l, (*vec)[3]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushnil(l);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleVec4NewIndex(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *key = luaL_checkstring(l, 2);
|
|
||||||
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
||||||
|
|
||||||
vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt");
|
|
||||||
assertNotNull(vec, "Vec4 pointer cannot be NULL.");
|
|
||||||
|
|
||||||
if(stringCompare(key, "x") == 0) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec4 x component must be a number.");
|
|
||||||
}
|
|
||||||
(*vec)[0] = (float_t)lua_tonumber(l, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "y") == 0) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec4 y component must be a number.");
|
|
||||||
}
|
|
||||||
(*vec)[1] = (float_t)lua_tonumber(l, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "z") == 0) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec4 z component must be a number.");
|
|
||||||
}
|
|
||||||
(*vec)[2] = (float_t)lua_tonumber(l, 3);
|
|
||||||
return 0;
|
|
||||||
} else if(stringCompare(key, "w") == 0) {
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Vec4 w component must be a number.");
|
|
||||||
}
|
|
||||||
(*vec)[3] = (float_t)lua_tonumber(l, 3);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
luaL_error(l, "Invalid key for vec4: %s", key);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleVec4ToString(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
vec4 *vec = (vec4 *)luaL_checkudata(l, 1, "vec4_mt");
|
|
||||||
assertNotNull(vec, "Vec4 pointer cannot be NULL.");
|
|
||||||
|
|
||||||
char buf[128];
|
|
||||||
snprintf(
|
|
||||||
buf, sizeof(buf),
|
|
||||||
"vec4(%.3f, %.3f, %.3f, %.3f)",
|
|
||||||
(*vec)[0], (*vec)[1], (*vec)[2], (*vec)[3]
|
|
||||||
);
|
|
||||||
lua_pushstring(l, buf);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the GLM module with the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register the module with.
|
|
||||||
*/
|
|
||||||
void moduleGLM(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new vec3 structure in Lua.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec3Create(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua __index metamethod for vec3 structure.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec3Index(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua __newindex metamethod for vec3 structure.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec3NewIndex(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua __tostring metamethod for vec3 structure.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec3ToString(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new vec4 structure in Lua.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec4Create(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua __index metamethod for vec4 structure.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec4Index(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua __newindex metamethod for vec4 structure.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec4NewIndex(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua __tostring metamethod for vec4 structure.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleVec4ToString(lua_State *l);
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "modulescreen.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/screen/screen.h"
|
|
||||||
|
|
||||||
void moduleScreen(scriptcontext_t *context) {
|
|
||||||
assertNotNull(context, "Context cannot be NULL.");
|
|
||||||
|
|
||||||
lua_register(context->luaState, "screenGetWidth", moduleScreenGetWidth);
|
|
||||||
lua_register(context->luaState, "screenGetHeight", moduleScreenGetHeight);
|
|
||||||
lua_register(context->luaState, "screenSetBackground", moduleScreenSetBackground);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleScreenGetWidth(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
lua_pushnumber(L, SCREEN.width);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleScreenGetHeight(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
lua_pushnumber(L, SCREEN.height);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleScreenSetBackground(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
|
|
||||||
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");
|
|
||||||
SCREEN.background = *color;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,40 +1,74 @@
|
|||||||
/**
|
/**
|
||||||
* 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/display/modulecolor.h"
|
||||||
|
#include "display/screen/screen.h"
|
||||||
|
|
||||||
/**
|
static scriptproto_t MODULE_SCREEN_PROTO;
|
||||||
* Registers the Screen module with the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register the module with.
|
|
||||||
*/
|
|
||||||
void moduleScreen(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleScreenGetWidth) {
|
||||||
* Gets the current screen width.
|
return jerry_number(SCREEN.width);
|
||||||
*
|
}
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Count of return values.
|
|
||||||
*/
|
|
||||||
int moduleScreenGetWidth(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleScreenGetHeight) {
|
||||||
* Gets the current screen height.
|
return jerry_number(SCREEN.height);
|
||||||
*
|
}
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Count of return values.
|
|
||||||
*/
|
|
||||||
int moduleScreenGetHeight(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleScreenGetAspect) {
|
||||||
* Sets the screen background color.
|
return jerry_number(SCREEN.aspect);
|
||||||
*
|
}
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Count of return values.
|
moduleBaseFunction(moduleScreenGetBackground) {
|
||||||
*/
|
return moduleColorMakeObject(SCREEN.background);
|
||||||
int moduleScreenSetBackground(lua_State *L);
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleScreenSetBackground) {
|
||||||
|
if(argc < 1 || !jerry_value_is_object(args[0])) {
|
||||||
|
return moduleBaseThrow("Screen background color must be a color object");
|
||||||
|
}
|
||||||
|
color_t *color = (color_t*)scriptProtoGetValue(
|
||||||
|
&MODULE_COLOR_PROTO, args[0]
|
||||||
|
);
|
||||||
|
if(!color) {
|
||||||
|
return moduleBaseThrow("Background must be a valid color object");
|
||||||
|
}
|
||||||
|
memoryCopy(&SCREEN.background, color, sizeof(color_t));
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleScreenToString) {
|
||||||
|
char_t buf[128];
|
||||||
|
stringFormat(
|
||||||
|
buf, sizeof(buf),
|
||||||
|
"{ \"width\": %d, \"height\": %d, \"aspect\": %.2f }",
|
||||||
|
SCREEN.width, SCREEN.height, SCREEN.aspect
|
||||||
|
);
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleScreen(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_SCREEN_PROTO, "Screen", sizeof(screen_t), NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_SCREEN_PROTO, "width", moduleScreenGetWidth, NULL
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_SCREEN_PROTO, "height", moduleScreenGetHeight, NULL
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_SCREEN_PROTO, "aspect", moduleScreenGetAspect, NULL
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_SCREEN_PROTO, "background",
|
||||||
|
moduleScreenGetBackground, moduleScreenSetBackground
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineToString(&MODULE_SCREEN_PROTO, moduleScreenToString);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "moduleshader.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/shader/shader.h"
|
|
||||||
#include "display/shader/shaderunlit.h"
|
|
||||||
|
|
||||||
void moduleShader(scriptcontext_t *context) {
|
|
||||||
assertNotNull(context, "Context cannot be NULL.");
|
|
||||||
|
|
||||||
// Shader unlit defs
|
|
||||||
lua_pushlightuserdata(context->luaState, &SHADER_UNLIT);
|
|
||||||
lua_setglobal(context->luaState, "SHADER_UNLIT");
|
|
||||||
|
|
||||||
lua_pushstring(context->luaState, SHADER_UNLIT_PROJECTION);
|
|
||||||
lua_setglobal(context->luaState, "SHADER_UNLIT_PROJECTION");
|
|
||||||
lua_pushstring(context->luaState, SHADER_UNLIT_VIEW);
|
|
||||||
lua_setglobal(context->luaState, "SHADER_UNLIT_VIEW");
|
|
||||||
lua_pushstring(context->luaState, SHADER_UNLIT_MODEL);
|
|
||||||
lua_setglobal(context->luaState, "SHADER_UNLIT_MODEL");
|
|
||||||
lua_pushstring(context->luaState, SHADER_UNLIT_TEXTURE);
|
|
||||||
lua_setglobal(context->luaState, "SHADER_UNLIT_TEXTURE");
|
|
||||||
|
|
||||||
// Shader methods
|
|
||||||
lua_register(context->luaState, "shaderBind", moduleShaderBind);
|
|
||||||
lua_register(context->luaState, "shaderSetMatrix", moduleShaderSetMatrix);
|
|
||||||
lua_register(context->luaState, "shaderSetTexture", moduleShaderSetTexture);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleShaderBind(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
// Should be passed a shader userdata pointer only.
|
|
||||||
shader_t *shader = (shader_t *)lua_touserdata(l, 1);
|
|
||||||
assertNotNull(shader, "Shader pointer cannot be NULL.");
|
|
||||||
|
|
||||||
errorret_t ret = shaderBind(shader);
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
luaL_error(l, "Failed to bind shader: %s", ret.state->message);
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleShaderSetMatrix(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
// Expect shader, string and matrix.
|
|
||||||
if(!lua_isuserdata(l, 1)) {
|
|
||||||
luaL_error(l, "First argument must be a shader_mt userdata.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!lua_isstring(l, 2)) {
|
|
||||||
luaL_error(l, "Second argument must be a string.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!lua_isuserdata(l, 3)) {
|
|
||||||
luaL_error(l, "Third argument must be a mat4_mt userdata.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
shader_t *shader = (shader_t *)lua_touserdata(l, 1);
|
|
||||||
assertNotNull(shader, "Shader pointer cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *uniformName = luaL_checkstring(l, 2);
|
|
||||||
assertStrLenMin(uniformName, 1, "Uniform name cannot be empty.");
|
|
||||||
|
|
||||||
mat4 *mat = (mat4 *)lua_touserdata(l, 3);
|
|
||||||
assertNotNull(mat, "Matrix pointer cannot be NULL.");
|
|
||||||
|
|
||||||
errorret_t ret = shaderSetMatrix(shader, uniformName, *mat);
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
luaL_error(l, "Failed to set shader matrix: %s", ret.state->message);
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleShaderSetTexture(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
shader_t *shader = (shader_t *)lua_touserdata(l, 1);
|
|
||||||
assertNotNull(shader, "Shader pointer cannot be NULL.");
|
|
||||||
|
|
||||||
const char_t *uniformName = luaL_checkstring(l, 2);
|
|
||||||
assertStrLenMin(uniformName, 1, "Uniform name cannot be empty.");
|
|
||||||
|
|
||||||
texture_t *texture;
|
|
||||||
// Texture can be Nil or a pointer, if not nil it must be a texture pointer.
|
|
||||||
if(lua_isnil(l, 3)) {
|
|
||||||
texture = NULL;
|
|
||||||
} else if(lua_isuserdata(l, 3)) {
|
|
||||||
texture = (texture_t *)lua_touserdata(l, 3);
|
|
||||||
assertNotNull(texture, "Texture pointer cannot be NULL.");
|
|
||||||
} else {
|
|
||||||
luaL_error(l, "Third argument must be a texture_mt userdata or nil.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t ret = shaderSetTexture(shader, uniformName, texture);
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
luaL_error(l, "Failed to set shader texture: %s", ret.state->message);
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register shader functions to the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register shader functions to.
|
|
||||||
*/
|
|
||||||
void moduleShader(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for binding a shader.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleShaderBind(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for setting a matrix uniform in a shader.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleShaderSetMatrix(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for setting a texture uniform in a shader.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleShaderSetTexture(lua_State *l);
|
|
||||||
|
|
||||||
errorret_t doThing();
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "modulespritebatch.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void moduleSpriteBatch(scriptcontext_t *context) {
|
|
||||||
lua_register(context->luaState, "spriteBatchFlush", moduleSpriteBatchFlush);
|
|
||||||
lua_register(context->luaState, "spriteBatchClear", moduleSpriteBatchClear);
|
|
||||||
lua_register(context->luaState, "spriteBatchPush", moduleSpriteBatchPush);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleSpriteBatchFlush(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
|
|
||||||
spriteBatchFlush();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleSpriteBatchClear(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
|
|
||||||
spriteBatchClear();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleSpriteBatchPush(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
|
|
||||||
// MinX, MinY, MaxX, MaxY
|
|
||||||
if(
|
|
||||||
!lua_isnumber(L, 1) || !lua_isnumber(L, 2) || !lua_isnumber(L, 3) ||
|
|
||||||
!lua_isnumber(L, 4)
|
|
||||||
) {
|
|
||||||
return luaL_error(L, "Sprite coordinates must be numbers");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Color (struct) or nil for white
|
|
||||||
color_t *color = NULL;
|
|
||||||
if(lua_gettop(L) < 5 || lua_isnil(L, 5)) {
|
|
||||||
// Allow NULL
|
|
||||||
} else if(lua_isuserdata(L, 5)) {
|
|
||||||
color = (color_t*)luaL_checkudata(L, 5, "color_mt");
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "Sprite color must be a color struct or nil");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional UV min and maxes, defaults to 0,0 -> 1,1
|
|
||||||
float_t u0 = 0.0f;
|
|
||||||
float_t v0 = 0.0f;
|
|
||||||
float_t u1 = 1.0f;
|
|
||||||
float_t v1 = 1.0f;
|
|
||||||
|
|
||||||
if(lua_gettop(L) >= 7) {
|
|
||||||
if(!lua_isnumber(L, 6) || !lua_isnumber(L, 7)) {
|
|
||||||
return luaL_error(L, "Sprite UV min coordinates must be numbers");
|
|
||||||
}
|
|
||||||
|
|
||||||
u0 = (float_t)lua_tonumber(L, 6);
|
|
||||||
v0 = (float_t)lua_tonumber(L, 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(lua_gettop(L) >= 9) {
|
|
||||||
if(!lua_isnumber(L, 8) || !lua_isnumber(L, 9)) {
|
|
||||||
return luaL_error(L, "Sprite UV max coordinates must be numbers");
|
|
||||||
}
|
|
||||||
|
|
||||||
u1 = (float_t)lua_tonumber(L, 8);
|
|
||||||
v1 = (float_t)lua_tonumber(L, 9);
|
|
||||||
}
|
|
||||||
|
|
||||||
float_t minX = (float_t)lua_tonumber(L, 1);
|
|
||||||
float_t minY = (float_t)lua_tonumber(L, 2);
|
|
||||||
float_t maxX = (float_t)lua_tonumber(L, 3);
|
|
||||||
float_t maxY = (float_t)lua_tonumber(L, 4);
|
|
||||||
|
|
||||||
errorret_t ret = spriteBatchPush(
|
|
||||||
minX,
|
|
||||||
minY,
|
|
||||||
maxX,
|
|
||||||
maxY,
|
|
||||||
#if MESH_ENABLE_COLOR
|
|
||||||
color == NULL ? COLOR_WHITE : *color,
|
|
||||||
#endif
|
|
||||||
u0,
|
|
||||||
v0,
|
|
||||||
u1,
|
|
||||||
v1
|
|
||||||
);
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
int err = luaL_error(L,
|
|
||||||
"Failed to push sprite to batch: %s",
|
|
||||||
ret.state->message
|
|
||||||
);
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register sprite batch functions to the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register sprite batch functions to.
|
|
||||||
*/
|
|
||||||
void moduleSpriteBatch(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for flushing the sprite batch.
|
|
||||||
*
|
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleSpriteBatchFlush(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for clearing the sprite batch.
|
|
||||||
*
|
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleSpriteBatchClear(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for pushing a sprite to the sprite batch.
|
|
||||||
*
|
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleSpriteBatchPush(lua_State *L);
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "moduletext.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/text/text.h"
|
|
||||||
#include "display/spritebatch/spritebatch.h"
|
|
||||||
|
|
||||||
void moduleText(scriptcontext_t *context) {
|
|
||||||
assertNotNull(context, "Script context is null");
|
|
||||||
|
|
||||||
lua_register(context->luaState, "textDraw", moduleTextDraw);
|
|
||||||
lua_register(context->luaState, "textMeasure", moduleTextMeasure);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTextDraw(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
|
|
||||||
// Position.
|
|
||||||
if(!lua_isnumber(L, 1)) {
|
|
||||||
return luaL_error(L, "X position must be a number");
|
|
||||||
}
|
|
||||||
if(!lua_isnumber(L, 2)) {
|
|
||||||
return luaL_error(L, "Y position must be a number");
|
|
||||||
}
|
|
||||||
|
|
||||||
const float_t x = (float_t)lua_tonumber(L, 1);
|
|
||||||
const float_t y = (float_t)lua_tonumber(L, 2);
|
|
||||||
|
|
||||||
// String
|
|
||||||
if(!lua_isstring(L, 3)) {
|
|
||||||
return luaL_error(L, "Text to draw must be a string");
|
|
||||||
}
|
|
||||||
const char_t *text = (const char_t*)lua_tostring(L, 3);
|
|
||||||
|
|
||||||
// Optional color
|
|
||||||
color_t *color = NULL;
|
|
||||||
if(lua_gettop(L) < 4 || lua_isnil(L, 4)) {
|
|
||||||
// Allow NULL
|
|
||||||
} else if(lua_isuserdata(L, 4)) {
|
|
||||||
color = (color_t*)luaL_checkudata(L, 4, "color_mt");
|
|
||||||
} else {
|
|
||||||
return luaL_error(L, "Sprite color must be a color struct or nil");
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now, use the default font tileset and texture.
|
|
||||||
errorret_t ret = textDraw(
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
text,
|
|
||||||
color == NULL ? COLOR_WHITE : *color,
|
|
||||||
&DEFAULT_FONT_TILESET,
|
|
||||||
&DEFAULT_FONT_TEXTURE
|
|
||||||
);
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
luaL_error(L, "Failed to draw text");
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = spriteBatchFlush();
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
luaL_error(L, "Failed to flush sprite batch after drawing text");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTextMeasure(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state is null");
|
|
||||||
|
|
||||||
// String
|
|
||||||
if(!lua_isstring(L, 1)) {
|
|
||||||
return luaL_error(L, "Text to measure must be a string");
|
|
||||||
}
|
|
||||||
const char_t *text = (const char_t*)lua_tostring(L, 1);
|
|
||||||
|
|
||||||
int32_t width = 0;
|
|
||||||
int32_t height = 0;
|
|
||||||
textMeasure(
|
|
||||||
text,
|
|
||||||
&DEFAULT_FONT_TILESET,
|
|
||||||
&width,
|
|
||||||
&height
|
|
||||||
);
|
|
||||||
|
|
||||||
lua_pushnumber(L, width);
|
|
||||||
lua_pushnumber(L, height);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register text rendering functions to the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register text functions to.
|
|
||||||
*/
|
|
||||||
void moduleText(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for drawing text.
|
|
||||||
*
|
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleTextDraw(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for measuring text dimensions.
|
|
||||||
*
|
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleTextMeasure(lua_State *L);
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "moduletexture.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/texture/texture.h"
|
|
||||||
#include "asset/loader/display/assettextureloader.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
|
|
||||||
void moduleTexture(scriptcontext_t *ctx) {
|
|
||||||
assertNotNull(ctx, "Script context cannot be null");
|
|
||||||
|
|
||||||
lua_State *l = ctx->luaState;
|
|
||||||
assertNotNull(l, "Lua state cannot be null");
|
|
||||||
|
|
||||||
// Create metatable for texture structure.
|
|
||||||
if(luaL_newmetatable(l, "texture_mt") == 1) {
|
|
||||||
lua_pushcfunction(l, moduleTextureIndex);
|
|
||||||
lua_setfield(l, -2, "__index");
|
|
||||||
|
|
||||||
lua_pushcfunction(l, moduleTextureToString);
|
|
||||||
lua_setfield(l, -2, "__tostring");
|
|
||||||
|
|
||||||
lua_pushcfunction(l, moduleTextureGC);
|
|
||||||
lua_setfield(l, -2, "__gc");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture formats
|
|
||||||
lua_pushnumber(l, TEXTURE_FORMAT_RGBA);
|
|
||||||
lua_setglobal(l, "TEXTURE_FORMAT_RGBA");
|
|
||||||
|
|
||||||
lua_register(ctx->luaState, "textureLoad", moduleTextureLoad);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTextureIndex(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt");
|
|
||||||
assertNotNull(tex, "Texture pointer cannot be NULL.");
|
|
||||||
|
|
||||||
const char *key = luaL_checkstring(l, 2);
|
|
||||||
assertNotNull(key, "Key cannot be NULL.");
|
|
||||||
|
|
||||||
if(stringCompare(key, "width") == 0) {
|
|
||||||
lua_pushnumber(l, tex->width);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "height") == 0) {
|
|
||||||
lua_pushnumber(l, tex->height);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushnil(l);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTextureToString(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt");
|
|
||||||
assertNotNull(tex, "Texture pointer cannot be NULL.");
|
|
||||||
|
|
||||||
char buffer[64];
|
|
||||||
snprintf(buffer, sizeof(buffer), "Texture(%dx%d)", tex->width, tex->height);
|
|
||||||
lua_pushstring(l, buffer);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTextureGC(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
texture_t *tex = (texture_t *)luaL_checkudata(l, 1, "texture_mt");
|
|
||||||
assertNotNull(tex, "Texture pointer cannot be NULL.");
|
|
||||||
|
|
||||||
textureDispose(tex);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTextureLoad(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
if(!lua_isstring(l, 1)) {
|
|
||||||
luaL_error(l, "First argument must be a string filename.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
assertNotNull(filename, "Filename cannot be NULL.");
|
|
||||||
assertStrLenMin(filename, 1, "Filename cannot be empty.");
|
|
||||||
|
|
||||||
// Create texture owned to lua
|
|
||||||
texture_t *tex = (texture_t *)lua_newuserdata(l, sizeof(texture_t));
|
|
||||||
memoryZero(tex, sizeof(texture_t));
|
|
||||||
|
|
||||||
textureformat_t format = (textureformat_t)lua_tonumber(l, 2);
|
|
||||||
|
|
||||||
errorret_t ret = assetTextureLoad(filename, tex, format);
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
luaL_error(l, "Failed to load texture asset: %s", filename);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set metatable
|
|
||||||
luaL_getmetatable(l, "texture_mt");
|
|
||||||
lua_setmetatable(l, -2);
|
|
||||||
|
|
||||||
// Return the texture
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
void moduleTexture(scriptcontext_t *ctx);
|
|
||||||
int moduleTextureIndex(lua_State *l);
|
|
||||||
int moduleTextureToString(lua_State *l);
|
|
||||||
int moduleTextureGC(lua_State *l);
|
|
||||||
int moduleTextureLoad(lua_State *l);
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "moduletileset.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "display/texture/tileset.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
#include "asset/loader/display/assettilesetloader.h"
|
|
||||||
|
|
||||||
void moduleTileset(scriptcontext_t *ctx) {
|
|
||||||
assertNotNull(ctx, "Script context cannot be NULL");
|
|
||||||
|
|
||||||
// Tileset metatable
|
|
||||||
if(luaL_newmetatable(ctx->luaState, "tileset_mt")) {
|
|
||||||
lua_pushcfunction(ctx->luaState, moduleTilesetIndex);
|
|
||||||
lua_setfield(ctx->luaState, -2, "__index");
|
|
||||||
|
|
||||||
lua_pushcfunction(ctx->luaState, moduleTilesetToString);
|
|
||||||
lua_setfield(ctx->luaState, -2, "__tostring");
|
|
||||||
}
|
|
||||||
lua_pop(ctx->luaState, 1); // Pop the metatable
|
|
||||||
|
|
||||||
lua_register(ctx->luaState, "tilesetLoad", moduleTilesetLoad);
|
|
||||||
lua_register(ctx->luaState, "tilesetTileGetUV", moduleTilesetTileGetUV);
|
|
||||||
lua_register(
|
|
||||||
ctx->luaState, "tilesetPositionGetUV", moduleTilesetPositionGetUV
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTilesetIndex(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
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) {
|
|
||||||
lua_pushnumber(l, ts->tileWidth);
|
|
||||||
return 1;
|
|
||||||
} else if(stringCompare(key, "tileHeight") == 0) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTilesetToString(lua_State *l) {
|
|
||||||
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
|
|
||||||
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
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTilesetTileGetUV(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
if(!lua_isuserdata(l, 1)) {
|
|
||||||
luaL_error(l, "First argument must be a tileset userdata.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
|
|
||||||
assertNotNull(ts, "Tileset pointer cannot be NULL.");
|
|
||||||
|
|
||||||
if(!lua_isnumber(l, 2)) {
|
|
||||||
luaL_error(l, "Second arguments must be tile index.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
uint16_t tileIndex = (uint16_t)lua_tonumber(l, 2);
|
|
||||||
|
|
||||||
// Create vec4 that lua owns
|
|
||||||
vec4 *uv = (vec4 *)lua_newuserdata(l, sizeof(vec4));
|
|
||||||
tilesetTileGetUV(ts, tileIndex, *uv);
|
|
||||||
|
|
||||||
// Set metatable
|
|
||||||
luaL_getmetatable(l, "vec4_mt");
|
|
||||||
lua_setmetatable(l, -2);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTilesetPositionGetUV(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
if(!lua_isuserdata(l, 1)) {
|
|
||||||
luaL_error(l, "First argument must be a tileset userdata.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
tileset_t *ts = (tileset_t *)luaL_checkudata(l, 1, "tileset_mt");
|
|
||||||
assertNotNull(ts, "Tileset pointer cannot be NULL.");
|
|
||||||
|
|
||||||
if(!lua_isnumber(l, 2)) {
|
|
||||||
luaL_error(l, "Second arguments must be column number.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
uint16_t column = (uint16_t)lua_tonumber(l, 2);
|
|
||||||
|
|
||||||
if(!lua_isnumber(l, 3)) {
|
|
||||||
luaL_error(l, "Third arguments must be row number.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
uint16_t row = (uint16_t)lua_tonumber(l, 3);
|
|
||||||
|
|
||||||
// Create vec4 that lua owns
|
|
||||||
vec4 *uv = (vec4 *)lua_newuserdata(l, sizeof(vec4));
|
|
||||||
tilesetPositionGetUV(ts, column, row, *uv);
|
|
||||||
|
|
||||||
// Set metatable
|
|
||||||
luaL_getmetatable(l, "vec4_mt");
|
|
||||||
lua_setmetatable(l, -2);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleTilesetLoad(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL.");
|
|
||||||
|
|
||||||
if(!lua_isstring(l, 1)) {
|
|
||||||
luaL_error(l, "First argument must be a string filename.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char_t *filename = lua_tostring(l, 1);
|
|
||||||
assertNotNull(filename, "Filename cannot be NULL.");
|
|
||||||
assertStrLenMin(filename, 1, "Filename cannot be empty.");
|
|
||||||
|
|
||||||
// Create texture owned to lua
|
|
||||||
tileset_t *tileset = (tileset_t *)lua_newuserdata(l, sizeof(tileset_t));
|
|
||||||
memoryZero(tileset, sizeof(tileset_t));
|
|
||||||
|
|
||||||
errorret_t ret = assetTilesetLoad(filename, tileset);
|
|
||||||
if(ret.code != ERROR_OK) {
|
|
||||||
errorCatch(errorPrint(ret));
|
|
||||||
luaL_error(l, "Failed to load tileset asset: %s", filename);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set metatable
|
|
||||||
luaL_getmetatable(l, "tileset_mt");
|
|
||||||
lua_setmetatable(l, -2);
|
|
||||||
|
|
||||||
// Return the tileset
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the tileset module in the scripting context.
|
|
||||||
*
|
|
||||||
* @param ctx The scripting context to register the module in.
|
|
||||||
*/
|
|
||||||
void moduleTileset(scriptcontext_t *ctx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __index metamethod for tileset userdata.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return The number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleTilesetIndex(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __tostring metamethod for tileset userdata.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return The number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleTilesetToString(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua function to get the UV coordinates for a tile index in a tileset.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return The number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleTilesetTileGetUV(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua function to get the UV coordinates for a tile position in a tileset.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return The number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleTilesetPositionGetUV(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lua function to load a tileset from a texture and tile dimensions.
|
|
||||||
*
|
|
||||||
* @param l The Lua state.
|
|
||||||
* @return The number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleTilesetLoad(lua_State *l);
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "engine/engine.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_ENGINE_PROTO;
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEngineExit) {
|
||||||
|
ENGINE.running = false;
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleEngine(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_ENGINE_PROTO, "Engine",
|
||||||
|
sizeof(uint8_t), NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_ENGINE_PROTO, "exit", moduleEngineExit
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
#include "entity/component/display/entitycamera.h"
|
||||||
|
#include "moduleentityposition.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_ENTITY_CAMERA_PROTO;
|
||||||
|
|
||||||
|
static entitycamera_t * moduleEntityCameraGet(
|
||||||
|
const jerry_call_info_t *callInfo
|
||||||
|
) {
|
||||||
|
componenthandle_t *h = scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!h) return NULL;
|
||||||
|
return (entitycamera_t*)componentGetData(
|
||||||
|
h->eid, h->cid, COMPONENT_TYPE_CAMERA
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetZNear) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam) return jerry_undefined();
|
||||||
|
return jerry_number(cam->nearClip);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetZFar) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam) return jerry_undefined();
|
||||||
|
return jerry_number(cam->farClip);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetOrthoTop) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
return jerry_number(cam->orthographic.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetOrthoBottom) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
return jerry_number(cam->orthographic.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetOrthoLeft) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
return jerry_number(cam->orthographic.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetOrthoRight) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
return jerry_number(cam->orthographic.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetFov) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || (
|
||||||
|
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE &&
|
||||||
|
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
||||||
|
)) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
return jerry_number(cam->perspective.fov);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraGetProjectionType) {
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam) return jerry_undefined();
|
||||||
|
return jerry_number(cam->projType);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetZNear) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam) return jerry_undefined();
|
||||||
|
cam->nearClip = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetZFar) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam) return jerry_undefined();
|
||||||
|
cam->farClip = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetOrthoTop) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
cam->orthographic.top = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetOrthoBottom) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
cam->orthographic.bottom = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetOrthoLeft) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
cam->orthographic.left = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetOrthoRight) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
cam->orthographic.right = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetFov) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam || (
|
||||||
|
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE &&
|
||||||
|
cam->projType != ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
||||||
|
)) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
cam->perspective.fov = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraSetProjectionType) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected a number");
|
||||||
|
}
|
||||||
|
entitycamera_t *cam = moduleEntityCameraGet(callInfo);
|
||||||
|
if(!cam) return jerry_undefined();
|
||||||
|
int32_t projType = (int32_t)jerry_value_as_number(args[0]);
|
||||||
|
if(
|
||||||
|
projType < ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
|
||||||
|
projType > ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
|
||||||
|
) {
|
||||||
|
return moduleBaseThrow("Invalid projection type");
|
||||||
|
}
|
||||||
|
cam->projType = (entitycameraprojectiontype_t)projType;
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityCameraAdd) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
|
||||||
|
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_CAMERA);
|
||||||
|
componenthandle_t h = { .eid = id, .cid = comp };
|
||||||
|
return scriptProtoCreateValue(&MODULE_ENTITY_CAMERA_PROTO, &h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleEntityCAMERA(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, NULL, sizeof(componenthandle_t), NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "zNear",
|
||||||
|
moduleEntityCameraGetZNear, moduleEntityCameraSetZNear
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "zFar",
|
||||||
|
moduleEntityCameraGetZFar, moduleEntityCameraSetZFar
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "orthoTop",
|
||||||
|
moduleEntityCameraGetOrthoTop, moduleEntityCameraSetOrthoTop
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "orthoBottom",
|
||||||
|
moduleEntityCameraGetOrthoBottom, moduleEntityCameraSetOrthoBottom
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "orthoLeft",
|
||||||
|
moduleEntityCameraGetOrthoLeft, moduleEntityCameraSetOrthoLeft
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "orthoRight",
|
||||||
|
moduleEntityCameraGetOrthoRight, moduleEntityCameraSetOrthoRight
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "fov",
|
||||||
|
moduleEntityCameraGetFov, moduleEntityCameraSetFov
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_CAMERA_PROTO, "projectionType",
|
||||||
|
moduleEntityCameraGetProjectionType, moduleEntityCameraSetProjectionType
|
||||||
|
);
|
||||||
|
|
||||||
|
moduleBaseSetInt(
|
||||||
|
"CAMERA_TYPE_ORTHOGRAPHIC",
|
||||||
|
ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
|
||||||
|
);
|
||||||
|
moduleBaseSetInt(
|
||||||
|
"CAMERA_TYPE_PERSPECTIVE",
|
||||||
|
ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
#include "entity/component/display/entitymaterial.h"
|
||||||
|
#include "display/color.h"
|
||||||
|
#include "moduleentityposition.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_ENTITY_MATERIAL_PROTO;
|
||||||
|
|
||||||
|
static entitymaterial_t * moduleEntityMaterialGet(
|
||||||
|
const jerry_call_info_t *callInfo
|
||||||
|
) {
|
||||||
|
componenthandle_t *h = scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_MATERIAL_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!h) return NULL;
|
||||||
|
return (entitymaterial_t*)componentGetData(
|
||||||
|
h->eid, h->cid, COMPONENT_TYPE_MATERIAL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityMaterialSetColor) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
if(!jerry_value_is_object(args[0])) {
|
||||||
|
return moduleBaseThrow("expected color object");
|
||||||
|
}
|
||||||
|
entitymaterial_t *mat = moduleEntityMaterialGet(callInfo);
|
||||||
|
if(!mat) return jerry_undefined();
|
||||||
|
|
||||||
|
jerry_value_t key;
|
||||||
|
jerry_value_t v;
|
||||||
|
color_t col;
|
||||||
|
|
||||||
|
key = jerry_string_sz("r");
|
||||||
|
v = jerry_object_get(args[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[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[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[0], key);
|
||||||
|
jerry_value_free(key);
|
||||||
|
col.a = (colorchannel8_t)jerry_value_as_number(v);
|
||||||
|
jerry_value_free(v);
|
||||||
|
|
||||||
|
mat->material.unlit.color = col;
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityMaterialAdd) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
|
||||||
|
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MATERIAL);
|
||||||
|
componenthandle_t h = { .eid = id, .cid = comp };
|
||||||
|
return scriptProtoCreateValue(&MODULE_ENTITY_MATERIAL_PROTO, &h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleEntityMATERIAL(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_ENTITY_MATERIAL_PROTO,
|
||||||
|
NULL,
|
||||||
|
sizeof(componenthandle_t),
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_MATERIAL_PROTO,
|
||||||
|
"setColor",
|
||||||
|
moduleEntityMaterialSetColor
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
#include "entity/component/display/entitymesh.h"
|
||||||
|
#include "moduleentityposition.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_ENTITY_MESH_PROTO;
|
||||||
|
|
||||||
|
static entitymesh_t * moduleEntityMeshGet(
|
||||||
|
const jerry_call_info_t *callInfo
|
||||||
|
) {
|
||||||
|
componenthandle_t *h = scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_MESH_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!h) return NULL;
|
||||||
|
return (entitymesh_t*)componentGetData(h->eid, h->cid, COMPONENT_TYPE_MESH);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityMeshAdd) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
|
||||||
|
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_MESH);
|
||||||
|
componenthandle_t h = { .eid = id, .cid = comp };
|
||||||
|
return scriptProtoCreateValue(&MODULE_ENTITY_MESH_PROTO, &h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleEntityMESH(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_ENTITY_MESH_PROTO, NULL,
|
||||||
|
sizeof(componenthandle_t), NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/module/math/modulevec3ref.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
#include "entity/component/physics/entityphysics.h"
|
||||||
|
#include "moduleentityposition.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_ENTITY_PHYSICS_PROTO;
|
||||||
|
|
||||||
|
static entityphysics_t * moduleEntityPhysicsGet(
|
||||||
|
const jerry_call_info_t *callInfo
|
||||||
|
) {
|
||||||
|
componenthandle_t *h = scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!h) return NULL;
|
||||||
|
return entityPhysicsGet(h->eid, h->cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsGetVelocity) {
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
return moduleVec3RefPush(phys->velocity, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsSetVelocity) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
vec3 v;
|
||||||
|
if(!moduleVec3AnyCheck(args[0], v)) {
|
||||||
|
return moduleBaseThrow("expected Vec3");
|
||||||
|
}
|
||||||
|
glm_vec3_copy(v, phys->velocity);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsGetOnGround) {
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
return jerry_boolean(phys->onGround);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsGetBodyType) {
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
return jerry_number((double)phys->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsSetBodyType) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
phys->type = (physicsbodytype_t)(int32_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsApplyImpulse) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
if(phys->type == PHYSICS_BODY_STATIC) return jerry_undefined();
|
||||||
|
vec3 impulse;
|
||||||
|
if(!moduleVec3Check(args[0], impulse)) {
|
||||||
|
return moduleBaseThrow("expected Vec3 impulse");
|
||||||
|
}
|
||||||
|
glm_vec3_add(phys->velocity, impulse, phys->velocity);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsSetShapeCube) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
vec3 half;
|
||||||
|
if(!moduleVec3Check(args[0], half)) {
|
||||||
|
return moduleBaseThrow("expected Vec3 halfExtents");
|
||||||
|
}
|
||||||
|
phys->shape.type = PHYSICS_SHAPE_CUBE;
|
||||||
|
glm_vec3_copy(half, phys->shape.data.cube.halfExtents);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsSetShapeSphere) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
phys->shape.type = PHYSICS_SHAPE_SPHERE;
|
||||||
|
phys->shape.data.sphere.radius = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsSetShapeCapsule) {
|
||||||
|
moduleBaseRequireArgs(2);
|
||||||
|
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
phys->shape.type = PHYSICS_SHAPE_CAPSULE;
|
||||||
|
phys->shape.data.capsule.radius = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
phys->shape.data.capsule.halfHeight =
|
||||||
|
(float_t)jerry_value_as_number(args[1]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsSetShapePlane) {
|
||||||
|
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
|
||||||
|
entityphysics_t *phys = moduleEntityPhysicsGet(callInfo);
|
||||||
|
if(!phys) return jerry_undefined();
|
||||||
|
vec3 normal;
|
||||||
|
if(!moduleVec3Check(args[0], normal)) {
|
||||||
|
return moduleBaseThrow("expected Vec3 normal");
|
||||||
|
}
|
||||||
|
phys->shape.type = PHYSICS_SHAPE_PLANE;
|
||||||
|
glm_vec3_copy(normal, phys->shape.data.plane.normal);
|
||||||
|
phys->shape.data.plane.distance = (float_t)jerry_value_as_number(args[1]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPhysicsAdd) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
|
||||||
|
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_PHYSICS);
|
||||||
|
componenthandle_t h = { .eid = id, .cid = comp };
|
||||||
|
return scriptProtoCreateValue(&MODULE_ENTITY_PHYSICS_PROTO, &h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleEntityPHYSICS(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, NULL,
|
||||||
|
sizeof(componenthandle_t), NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "velocity",
|
||||||
|
moduleEntityPhysicsGetVelocity, moduleEntityPhysicsSetVelocity
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "onGround",
|
||||||
|
moduleEntityPhysicsGetOnGround, NULL
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "bodyType",
|
||||||
|
moduleEntityPhysicsGetBodyType, moduleEntityPhysicsSetBodyType
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "applyImpulse",
|
||||||
|
moduleEntityPhysicsApplyImpulse
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "setShapeCube",
|
||||||
|
moduleEntityPhysicsSetShapeCube
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "setShapeSphere",
|
||||||
|
moduleEntityPhysicsSetShapeSphere
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "setShapeCapsule",
|
||||||
|
moduleEntityPhysicsSetShapeCapsule
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_PHYSICS_PROTO, "setShapePlane",
|
||||||
|
moduleEntityPhysicsSetShapePlane
|
||||||
|
);
|
||||||
|
|
||||||
|
moduleBaseSetInt("PHYSICS_BODY_STATIC", PHYSICS_BODY_STATIC);
|
||||||
|
moduleBaseSetInt("PHYSICS_BODY_DYNAMIC", PHYSICS_BODY_DYNAMIC);
|
||||||
|
moduleBaseSetInt("PHYSICS_BODY_KINEMATIC", PHYSICS_BODY_KINEMATIC);
|
||||||
|
|
||||||
|
moduleBaseSetInt("PHYSICS_SHAPE_CUBE", PHYSICS_SHAPE_CUBE);
|
||||||
|
moduleBaseSetInt("PHYSICS_SHAPE_SPHERE", PHYSICS_SHAPE_SPHERE);
|
||||||
|
moduleBaseSetInt("PHYSICS_SHAPE_CAPSULE", PHYSICS_SHAPE_CAPSULE);
|
||||||
|
moduleBaseSetInt("PHYSICS_SHAPE_PLANE", PHYSICS_SHAPE_PLANE);
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/module/math/modulevec3ref.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
#include "entity/component/display/entityposition.h"
|
||||||
|
|
||||||
|
// Shared component handle struct — defined once, used by all component modules.
|
||||||
|
#ifndef COMPONENT_HANDLE_DEFINED
|
||||||
|
#define COMPONENT_HANDLE_DEFINED
|
||||||
|
typedef struct {
|
||||||
|
entityid_t eid;
|
||||||
|
componentid_t cid;
|
||||||
|
} componenthandle_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_ENTITY_POSITION_PROTO;
|
||||||
|
|
||||||
|
static entityposition_t * moduleEntityPositionGet(
|
||||||
|
const jerry_call_info_t *callInfo
|
||||||
|
) {
|
||||||
|
componenthandle_t *h = scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_POSITION_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!h) return NULL;
|
||||||
|
return entityPositionGet(h->eid, h->cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionGetPosition) {
|
||||||
|
entityposition_t *pos = moduleEntityPositionGet(callInfo);
|
||||||
|
if(!pos) return jerry_undefined();
|
||||||
|
return moduleVec3RefPush(
|
||||||
|
pos->position, (void(*)(void*))entityPositionRebuild, pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionSetPosition) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
entityposition_t *pos = moduleEntityPositionGet(callInfo);
|
||||||
|
if(!pos) return jerry_undefined();
|
||||||
|
vec3 v;
|
||||||
|
if(!moduleVec3AnyCheck(args[0], v)) {
|
||||||
|
return moduleBaseThrow("expected Vec3");
|
||||||
|
}
|
||||||
|
glm_vec3_copy(v, pos->position);
|
||||||
|
entityPositionRebuild(pos);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionGetRotation) {
|
||||||
|
entityposition_t *pos = moduleEntityPositionGet(callInfo);
|
||||||
|
if(!pos) return jerry_undefined();
|
||||||
|
return moduleVec3RefPush(
|
||||||
|
pos->rotation, (void(*)(void*))entityPositionRebuild, pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionSetRotation) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
entityposition_t *pos = moduleEntityPositionGet(callInfo);
|
||||||
|
if(!pos) return jerry_undefined();
|
||||||
|
vec3 v;
|
||||||
|
if(!moduleVec3AnyCheck(args[0], v)) {
|
||||||
|
return moduleBaseThrow("expected Vec3");
|
||||||
|
}
|
||||||
|
glm_vec3_copy(v, pos->rotation);
|
||||||
|
entityPositionRebuild(pos);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionGetScale) {
|
||||||
|
entityposition_t *pos = moduleEntityPositionGet(callInfo);
|
||||||
|
if(!pos) return jerry_undefined();
|
||||||
|
return moduleVec3RefPush(
|
||||||
|
pos->scale, (void(*)(void*))entityPositionRebuild, pos
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionSetScale) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
entityposition_t *pos = moduleEntityPositionGet(callInfo);
|
||||||
|
if(!pos) return jerry_undefined();
|
||||||
|
vec3 v;
|
||||||
|
if(!moduleVec3AnyCheck(args[0], v)) {
|
||||||
|
return moduleBaseThrow("expected Vec3");
|
||||||
|
}
|
||||||
|
glm_vec3_copy(v, pos->scale);
|
||||||
|
entityPositionRebuild(pos);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionLookAt) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
entityposition_t *pos = moduleEntityPositionGet(callInfo);
|
||||||
|
if(!pos) return jerry_undefined();
|
||||||
|
vec3 target;
|
||||||
|
if(!moduleVec3AnyCheck(args[0], target)) {
|
||||||
|
return moduleBaseThrow("expected Vec3 target");
|
||||||
|
}
|
||||||
|
vec3 up = { 0.0f, 1.0f, 0.0f };
|
||||||
|
if(argc >= 2 && !moduleVec3AnyCheck(args[1], up)) {
|
||||||
|
return moduleBaseThrow("expected Vec3 up");
|
||||||
|
}
|
||||||
|
glm_lookat(pos->position, target, up, pos->transform);
|
||||||
|
entityPositionDecompose(pos);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityPositionAdd) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
entityid_t id = (entityid_t)jerry_value_as_number(args[0]);
|
||||||
|
componentid_t comp = entityAddComponent(id, COMPONENT_TYPE_POSITION);
|
||||||
|
componenthandle_t h = { .eid = id, .cid = comp };
|
||||||
|
return scriptProtoCreateValue(&MODULE_ENTITY_POSITION_PROTO, &h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleEntityPOSITION(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_ENTITY_POSITION_PROTO, NULL,
|
||||||
|
sizeof(componenthandle_t), NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_POSITION_PROTO, "position",
|
||||||
|
moduleEntityPositionGetPosition, moduleEntityPositionSetPosition
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_POSITION_PROTO, "rotation",
|
||||||
|
moduleEntityPositionGetRotation, moduleEntityPositionSetRotation
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_POSITION_PROTO, "scale",
|
||||||
|
moduleEntityPositionGetScale, moduleEntityPositionSetScale
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_POSITION_PROTO, "lookAt",
|
||||||
|
moduleEntityPositionLookAt
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "entity/entity.h"
|
||||||
|
#include "entity/entitymanager.h"
|
||||||
|
|
||||||
|
#include "component/moduleentityposition.h"
|
||||||
|
#include "component/moduleentitycamera.h"
|
||||||
|
#include "component/moduleentitymesh.h"
|
||||||
|
#include "component/moduleentitymaterial.h"
|
||||||
|
#include "component/moduleentityphysics.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
entityid_t id;
|
||||||
|
} entityscript_t;
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_ENTITY_PROTO;
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
moduleBaseFunction(moduleEntityGetId) {
|
||||||
|
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!inst) return jerry_undefined();
|
||||||
|
return jerry_number(inst->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter defined for each component type
|
||||||
|
static jerry_value_t moduleEntityGetComponent(
|
||||||
|
const jerry_call_info_t *callInfo,
|
||||||
|
const jerry_value_t args[],
|
||||||
|
const jerry_length_t argc,
|
||||||
|
const componenttype_t type,
|
||||||
|
scriptproto_t *proto
|
||||||
|
) {
|
||||||
|
assertNotNull(callInfo, "Call info must not be null");
|
||||||
|
assertTrue(argc >= 0, "Argc must be non-negative");
|
||||||
|
assertTrue(
|
||||||
|
type > COMPONENT_TYPE_NULL && type < COMPONENT_TYPE_COUNT,
|
||||||
|
"Invalid component type"
|
||||||
|
);
|
||||||
|
|
||||||
|
entityid_t entityId;
|
||||||
|
componentid_t compId;
|
||||||
|
|
||||||
|
// Get the entity script data.
|
||||||
|
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!inst) return jerry_undefined();
|
||||||
|
entityId = inst->id;
|
||||||
|
|
||||||
|
// Find the component ID of the requested type.
|
||||||
|
compId = entityGetComponent(entityId, type);
|
||||||
|
if(compId == 0xFF) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
componenthandle_t h = { .eid = entityId, .cid = compId };
|
||||||
|
return scriptProtoCreateValue(proto, &h);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define X(enumName, type, field, init, dispose) \
|
||||||
|
moduleBaseFunction(moduleEntityGet##enumName) { \
|
||||||
|
return moduleEntityGetComponent( \
|
||||||
|
callInfo, \
|
||||||
|
args, \
|
||||||
|
argc, \
|
||||||
|
COMPONENT_TYPE_##enumName, \
|
||||||
|
&MODULE_ENTITY_##enumName##_PROTO \
|
||||||
|
); \
|
||||||
|
}
|
||||||
|
#include "entity/componentlist.h"
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityToString) {
|
||||||
|
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!inst) return jerry_string_sz("Entity(?)");
|
||||||
|
|
||||||
|
char_t components[128];
|
||||||
|
size_t clen = 0;
|
||||||
|
bool_t first = true;
|
||||||
|
|
||||||
|
for(componenttype_t t = 1; t < COMPONENT_TYPE_COUNT; t++) {
|
||||||
|
if(entityGetComponent(inst->id, t) == 0xFF) continue;
|
||||||
|
if(!first) {
|
||||||
|
stringCopy(components + clen, ", ", sizeof(components) - clen);
|
||||||
|
clen += 2;
|
||||||
|
}
|
||||||
|
const char_t *name = COMPONENT_DEFINITIONS[t].enumName;
|
||||||
|
stringCopy(components + clen, name, sizeof(components) - clen);
|
||||||
|
clen += strlen(name);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char_t buf[256];
|
||||||
|
if(first) {
|
||||||
|
stringFormat(
|
||||||
|
buf, sizeof(buf),
|
||||||
|
"{ \"id\": %d, \"components\": [] }", inst->id
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
stringFormat(
|
||||||
|
buf, sizeof(buf),
|
||||||
|
"{ \"id\": %d, \"components\": [ %s ] }", inst->id, components
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
moduleBaseFunction(moduleEntityConstructor) {
|
||||||
|
entityscript_t *inst = (entityscript_t*)memoryAllocate(
|
||||||
|
sizeof(entityscript_t)
|
||||||
|
);
|
||||||
|
inst->id = entityManagerAdd();
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
callInfo->this_value, &MODULE_ENTITY_PROTO.info, inst
|
||||||
|
);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityAddComponent) {
|
||||||
|
if(argc < 1 || !jerry_value_is_number(args[0])) {
|
||||||
|
return moduleBaseThrow("Entity.add: expected a valid component type");
|
||||||
|
}
|
||||||
|
|
||||||
|
componenttype_t type = (componenttype_t)jerry_value_as_number(args[0]);
|
||||||
|
if(type <= COMPONENT_TYPE_NULL || type >= COMPONENT_TYPE_COUNT) {
|
||||||
|
return moduleBaseThrow("Entity.add: invalid component type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the entity script data.
|
||||||
|
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!inst) return moduleBaseThrow("Entity.add: invalid entity");
|
||||||
|
|
||||||
|
componentid_t id = entityAddComponent(inst->id, type);
|
||||||
|
return jerry_number(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityDisposeMethod) {
|
||||||
|
entityscript_t *inst = (entityscript_t*)scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!inst) return jerry_undefined();
|
||||||
|
entityDispose(inst->id);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleEntity(void) {
|
||||||
|
// Init the entity prototype
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_ENTITY_PROTO,
|
||||||
|
"Entity",
|
||||||
|
sizeof(entityscript_t),
|
||||||
|
moduleEntityConstructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineToString(&MODULE_ENTITY_PROTO, moduleEntityToString);
|
||||||
|
|
||||||
|
// Entity Methods
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_PROTO, "add", moduleEntityAddComponent
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_ENTITY_PROTO, "dispose", moduleEntityDisposeMethod
|
||||||
|
);
|
||||||
|
|
||||||
|
// Entity props
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_ENTITY_PROTO, "id", moduleEntityGetId, NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
// Init component type modules.
|
||||||
|
char_t buffer[64];
|
||||||
|
|
||||||
|
#define X(enumName, type, field, iMethod, dMethod) \
|
||||||
|
moduleEntity##enumName(); \
|
||||||
|
scriptProtoDefineProp( \
|
||||||
|
&MODULE_ENTITY_PROTO, \
|
||||||
|
COMPONENT_DEFINITIONS[COMPONENT_TYPE_##enumName].name, \
|
||||||
|
moduleEntityGet##enumName, \
|
||||||
|
NULL \
|
||||||
|
); \
|
||||||
|
snprintf( \
|
||||||
|
buffer, sizeof(buffer), \
|
||||||
|
"%s = %d\n", \
|
||||||
|
#enumName, \
|
||||||
|
COMPONENT_TYPE_##enumName \
|
||||||
|
); \
|
||||||
|
moduleBaseEval(buffer);
|
||||||
|
#include "entity/componentlist.h"
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
moduleevent.c
|
|
||||||
)
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "moduleevent.h"
|
|
||||||
#include "event/event.h"
|
|
||||||
#include "engine/engine.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
int moduleEventSubscribe(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
// State has user pointer to owning scriptcontext_t
|
|
||||||
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L);
|
|
||||||
assertNotNull(context, "Script context cannot be NULL");
|
|
||||||
|
|
||||||
// Expecting event pointer
|
|
||||||
if(!lua_islightuserdata(L, 1)) {
|
|
||||||
luaL_error(L, "eventSubscribe: Expected event pointer as first argument");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expecting callback function (Lua function)
|
|
||||||
if(!lua_isfunction(L, 2)) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Pass back to lua.
|
|
||||||
lua_pushnumber(L, id);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void moduleEvent(scriptcontext_t *context) {
|
|
||||||
// Reg functions
|
|
||||||
lua_register(context->luaState, "eventSubscribe", moduleEventSubscribe);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register event functions to the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register event functions to.
|
|
||||||
*/
|
|
||||||
void moduleEvent(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for subscribing to an event.
|
|
||||||
*/
|
|
||||||
int moduleEventSubscribe(lua_State *L);
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Copyright (c) 2025 Dominic Masters
|
|
||||||
#
|
|
||||||
# This software is released under the MIT License.
|
|
||||||
# https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
# Sources
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|
||||||
PUBLIC
|
|
||||||
moduleinput.c
|
|
||||||
)
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "moduleinput.h"
|
|
||||||
#include "input/input.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/string.h"
|
|
||||||
|
|
||||||
void moduleInput(scriptcontext_t *context) {
|
|
||||||
assertNotNull(context, "Script context cannot be NULL");
|
|
||||||
|
|
||||||
// Setup enums.
|
|
||||||
scriptContextExec(context, INPUT_ACTION_SCRIPT);
|
|
||||||
|
|
||||||
// Input values.
|
|
||||||
scriptContextExec(context,
|
|
||||||
""
|
|
||||||
#ifdef DUSK_INPUT_KEYBOARD
|
|
||||||
"INPUT_KEYBOARD = true\n"
|
|
||||||
#endif
|
|
||||||
#ifdef DUSK_INPUT_GAMEPAD
|
|
||||||
"INPUT_GAMEPAD = true\n"
|
|
||||||
#endif
|
|
||||||
#ifdef DUSK_INPUT_POINTER
|
|
||||||
"INPUT_POINTER = true\n"
|
|
||||||
#endif
|
|
||||||
#ifdef DUSK_INPUT_TOUCH
|
|
||||||
"INPUT_TOUCH = true\n"
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
|
|
||||||
// Metatable
|
|
||||||
if(luaL_newmetatable(context->luaState, "input_mt")) {
|
|
||||||
lua_pushcfunction(context->luaState, moduleInputIndex);
|
|
||||||
lua_setfield(context->luaState, -2, "__index");
|
|
||||||
}
|
|
||||||
lua_pop(context->luaState, 1);
|
|
||||||
|
|
||||||
// Events
|
|
||||||
lua_pushlightuserdata(context->luaState, &INPUT.eventPressed);
|
|
||||||
lua_setglobal(context->luaState, "INPUT_EVENT_PRESSED");
|
|
||||||
|
|
||||||
lua_pushlightuserdata(context->luaState, &INPUT.eventReleased);
|
|
||||||
lua_setglobal(context->luaState, "INPUT_EVENT_RELEASED");
|
|
||||||
|
|
||||||
// Bind methods
|
|
||||||
lua_register(context->luaState, "inputBind", moduleInputBind);
|
|
||||||
lua_register(context->luaState, "inputIsDown", moduleInputIsDown);
|
|
||||||
lua_register(context->luaState, "inputPressed", moduleInputPressed);
|
|
||||||
lua_register(context->luaState, "inputReleased", moduleInputReleased);
|
|
||||||
lua_register(context->luaState, "inputGetValue", moduleInputGetValue);
|
|
||||||
lua_register(context->luaState, "inputAxis", moduleInputAxis);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleInputIndex(lua_State *l) {
|
|
||||||
assertNotNull(l, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
const char_t *key = luaL_checkstring(l, 2);
|
|
||||||
assertStrLenMin(key, 1, "Key cannot be empty.");
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleInputBind(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
// Requires action and button.
|
|
||||||
if(!lua_isstring(L, 1)) {
|
|
||||||
luaL_error(L, "inputBind: Expected button name as first argument");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expect action ID
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate action
|
|
||||||
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
|
||||||
luaL_error(L, "inputBind: Invalid action ID %d with str %s", action, strBtn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get button by name
|
|
||||||
inputbutton_t btn = inputButtonGetByName(strBtn);
|
|
||||||
if(btn.type == INPUT_BUTTON_TYPE_NONE) {
|
|
||||||
luaL_error(L, "inputBind: Invalid button name '%s'", strBtn);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputBind(btn, action);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleInputIsDown(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
|
||||||
luaL_error(L, "inputIsDown: Invalid action ID %d", action);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, inputIsDown(action));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleInputPressed(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
|
||||||
luaL_error(L, "inputPressed: Invalid action ID %d", action);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, inputPressed(action));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleInputReleased(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(action < INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
|
||||||
luaL_error(L, "inputReleased: Invalid action ID %d", action);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushboolean(L, inputReleased(action));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleInputGetValue(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
if(!lua_isnumber(L, 1)) {
|
|
||||||
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) {
|
|
||||||
luaL_error(L, "inputGetValue: Invalid action ID %d", action);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushnumber(L, inputGetCurrentValue(action));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleInputAxis(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
if(!lua_isnumber(L, 1) || !lua_isnumber(L, 2)) {
|
|
||||||
luaL_error(L, "inputAxis: Expected two action IDs as arguments (neg, pos)");
|
|
||||||
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) {
|
|
||||||
luaL_error(L, "inputAxis: Invalid negative action ID %d", neg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(pos < INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) {
|
|
||||||
luaL_error(L, "inputAxis: Invalid positive action ID %d", pos);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushnumber(L, inputAxis(neg, pos));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,71 +1,161 @@
|
|||||||
/**
|
/**
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "script/scriptcontext.h"
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "script/module/math/modulevec2.h"
|
||||||
|
#include "input/input.h"
|
||||||
|
|
||||||
/**
|
static scriptproto_t MODULE_INPUT_PROTO;
|
||||||
* Register input functions to the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register input functions to.
|
|
||||||
*/
|
|
||||||
void moduleInput(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
// Static Methods
|
||||||
* Getter for input structure fields.
|
moduleBaseFunction(moduleInputBind) {
|
||||||
*
|
moduleBaseRequireArgs(2);
|
||||||
* @param l The Lua state.
|
moduleBaseRequireString(0);
|
||||||
*/
|
moduleBaseRequireNumber(1);
|
||||||
int moduleInputIndex(lua_State *l);
|
|
||||||
|
|
||||||
/**
|
char_t strBtn[128];
|
||||||
* Script binding for binding an input button to an action.
|
moduleBaseToString(args[0], strBtn, sizeof(strBtn));
|
||||||
*
|
if(strBtn[0] == '\0') {
|
||||||
* @param L The Lua state.
|
return moduleBaseThrow("Input.bind: button name cannot be empty");
|
||||||
* @return Number of return values on the Lua stack.
|
}
|
||||||
*/
|
|
||||||
int moduleInputBind(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[1]);
|
||||||
* Script binding for checking if an input action is currently pressed.
|
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||||
*
|
return moduleBaseThrow("Input.bind: invalid action");
|
||||||
* @param L The Lua state.
|
}
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleInputIsDown(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
inputbutton_t btn = inputButtonGetByName(strBtn);
|
||||||
* Script binding for checking if an input action was pressed this frame.
|
if(btn.type == INPUT_BUTTON_TYPE_NONE) {
|
||||||
*
|
return moduleBaseThrow("Input.bind: invalid button name");
|
||||||
* @param L The Lua state.
|
}
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleInputPressed(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
inputBind(btn, action);
|
||||||
* Script binding for checking if an input action was released this frame.
|
return jerry_undefined();
|
||||||
*
|
}
|
||||||
* @param L The Lua state.
|
|
||||||
* @return Number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleInputReleased(lua_State *L);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleInputIsDown) {
|
||||||
* Script binding for getting the value of an input axis.
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
*
|
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||||
* @param L The Lua state.
|
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||||
* @return Number of return values on the Lua stack.
|
return moduleBaseThrow("Input.isDown: invalid action");
|
||||||
*/
|
}
|
||||||
int moduleInputGetValue(lua_State *L);
|
return jerry_boolean(inputIsDown(action));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleInputPressed) {
|
||||||
* Script binding for inputAxis.
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
*
|
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||||
* @param L The Lua state.
|
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||||
* @return Number of return values on the Lua stack.
|
return moduleBaseThrow("Input.pressed: invalid action");
|
||||||
*/
|
}
|
||||||
int moduleInputAxis(lua_State *L);
|
return jerry_boolean(inputPressed(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleInputReleased) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||||
|
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.released: invalid action");
|
||||||
|
}
|
||||||
|
return jerry_boolean(inputReleased(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleInputGetValue) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
const inputaction_t action = (inputaction_t)jerry_value_as_number(args[0]);
|
||||||
|
if(action <= INPUT_ACTION_NULL || action >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.getValue: invalid action");
|
||||||
|
}
|
||||||
|
return jerry_number(inputGetCurrentValue(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleInputAxis) {
|
||||||
|
moduleBaseRequireArgs(2);
|
||||||
|
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
|
||||||
|
const inputaction_t neg = (inputaction_t)jerry_value_as_number(args[0]);
|
||||||
|
const inputaction_t pos = (inputaction_t)jerry_value_as_number(args[1]);
|
||||||
|
if(neg <= INPUT_ACTION_NULL || neg >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.axis: invalid negative action");
|
||||||
|
}
|
||||||
|
if(pos <= INPUT_ACTION_NULL || pos >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.axis: invalid positive action");
|
||||||
|
}
|
||||||
|
return jerry_number(inputAxis(neg, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleInputAxis2D) {
|
||||||
|
moduleBaseRequireArgs(4);
|
||||||
|
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
|
||||||
|
moduleBaseRequireNumber(2); moduleBaseRequireNumber(3);
|
||||||
|
const inputaction_t negX = (inputaction_t)jerry_value_as_number(args[0]);
|
||||||
|
const inputaction_t posX = (inputaction_t)jerry_value_as_number(args[1]);
|
||||||
|
const inputaction_t negY = (inputaction_t)jerry_value_as_number(args[2]);
|
||||||
|
const inputaction_t posY = (inputaction_t)jerry_value_as_number(args[3]);
|
||||||
|
if(negX <= INPUT_ACTION_NULL || negX >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.axis2D: invalid negX action");
|
||||||
|
}
|
||||||
|
if(posX <= INPUT_ACTION_NULL || posX >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.axis2D: invalid posX action");
|
||||||
|
}
|
||||||
|
if(negY <= INPUT_ACTION_NULL || negY >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.axis2D: invalid negY action");
|
||||||
|
}
|
||||||
|
if(posY <= INPUT_ACTION_NULL || posY >= INPUT_ACTION_COUNT) {
|
||||||
|
return moduleBaseThrow("Input.axis2D: invalid posY action");
|
||||||
|
}
|
||||||
|
vec2 result;
|
||||||
|
inputAxis2D(negX, posX, negY, posY, result);
|
||||||
|
return moduleVec2Push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleInput(void) {
|
||||||
|
moduleBaseEval(INPUT_ACTION_SCRIPT);
|
||||||
|
|
||||||
|
moduleBaseEval(
|
||||||
|
""
|
||||||
|
#ifdef DUSK_INPUT_KEYBOARD
|
||||||
|
"var INPUT_KEYBOARD = true;\n"
|
||||||
|
#endif
|
||||||
|
#ifdef DUSK_INPUT_GAMEPAD
|
||||||
|
"var INPUT_GAMEPAD = true;\n"
|
||||||
|
#endif
|
||||||
|
#ifdef DUSK_INPUT_POINTER
|
||||||
|
"var INPUT_POINTER = true;\n"
|
||||||
|
#endif
|
||||||
|
#ifdef DUSK_INPUT_TOUCH
|
||||||
|
"var INPUT_TOUCH = true;\n"
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_INPUT_PROTO, "Input", sizeof(uint8_t), NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_INPUT_PROTO, "bind", moduleInputBind
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_INPUT_PROTO, "isDown", moduleInputIsDown
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_INPUT_PROTO, "pressed", moduleInputPressed
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_INPUT_PROTO, "released", moduleInputReleased
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_INPUT_PROTO, "getValue", moduleInputGetValue
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_INPUT_PROTO, "axis", moduleInputAxis
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_INPUT_PROTO, "axis2D", moduleInputAxis2D
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "modulelocale.h"
|
|
||||||
#include "locale/localemanager.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
void moduleLocale(scriptcontext_t *context) {
|
|
||||||
assertNotNull(context, "Script context cannot be NULL");
|
|
||||||
|
|
||||||
// Execute the locale script definitions.
|
|
||||||
lua_register(context->luaState, "localeGetText", moduleLocaleGetText);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleLocaleGetText(lua_State *L) {
|
|
||||||
// Expect string param for the id
|
|
||||||
if(!lua_isstring(L, 1)) {
|
|
||||||
luaL_error(L, "Expected message ID as first argument");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char_t *id = lua_tostring(L, 1);
|
|
||||||
if(id == NULL || strlen(id) == 0) {
|
|
||||||
luaL_error(L, "Message ID cannot be NULL or empty");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optional plural param, default to 0
|
|
||||||
int32_t plural = 0;
|
|
||||||
int top = lua_gettop(L);
|
|
||||||
int argStart = 2;
|
|
||||||
|
|
||||||
if(top >= 2 && !lua_isnil(L, 2)) {
|
|
||||||
if(!lua_isnumber(L, 2)) {
|
|
||||||
luaL_error(L, "Expected plural as second argument");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
plural = (int32_t)lua_tointeger(L, 2);
|
|
||||||
if(plural < 0) {
|
|
||||||
luaL_error(L, "Plural cannot be negative");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
argStart = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build structured arg list from remaining Lua args
|
|
||||||
size_t argCount = (top >= argStart) ? (size_t)(top - argStart + 1) : 0;
|
|
||||||
#define MODULE_LOCALE_MAX_ARGS 16
|
|
||||||
assetlocalearg_t argsStack[MODULE_LOCALE_MAX_ARGS];
|
|
||||||
assetlocalearg_t *args = argsStack;
|
|
||||||
|
|
||||||
if(argCount > MODULE_LOCALE_MAX_ARGS) {
|
|
||||||
luaL_error(L, "Too many args (max %d)", MODULE_LOCALE_MAX_ARGS);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t i = 0; i < argCount; ++i) {
|
|
||||||
int luaIndex = argStart + (int)i;
|
|
||||||
|
|
||||||
if(lua_isinteger(L, luaIndex)) {
|
|
||||||
args[i].type = ASSET_LOCALE_ARG_INT;
|
|
||||||
args[i].intValue = (int32_t)lua_tonumber(L, luaIndex);
|
|
||||||
} else if(lua_isnumber(L, luaIndex)) {
|
|
||||||
args[i].type = ASSET_LOCALE_ARG_FLOAT;
|
|
||||||
args[i].floatValue = lua_tonumber(L, luaIndex);
|
|
||||||
} else if(lua_isstring(L, luaIndex)) {
|
|
||||||
args[i].type = ASSET_LOCALE_ARG_STRING;
|
|
||||||
args[i].stringValue = lua_tostring(L, luaIndex);
|
|
||||||
} else {
|
|
||||||
luaL_error(L, "Unsupported localization argument type");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char_t buffer[1024];
|
|
||||||
errorret_t err = localeManagerGetTextArgs(
|
|
||||||
id,
|
|
||||||
buffer,
|
|
||||||
sizeof(buffer),
|
|
||||||
plural,
|
|
||||||
args,
|
|
||||||
argCount
|
|
||||||
);
|
|
||||||
|
|
||||||
if(err.code != ERROR_OK) {
|
|
||||||
errorCatch(errorPrint(err));
|
|
||||||
luaL_error(L, "Failed to get localized text for ID '%s'", id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_pushstring(L, buffer);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "script/scriptcontext.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register locale functions to the given script context.
|
|
||||||
*
|
|
||||||
* @param context The script context to register locale functions to.
|
|
||||||
*/
|
|
||||||
void moduleLocale(scriptcontext_t *context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Script binding for getting a localized string.
|
|
||||||
*
|
|
||||||
* @param L The Lua state.
|
|
||||||
* @return The number of return values on the Lua stack.
|
|
||||||
*/
|
|
||||||
int moduleLocaleGetText(lua_State *L);
|
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "cglm/cglm.h"
|
||||||
|
#include "modulevec3.h"
|
||||||
|
#include "modulevec4.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_MAT4_PROTO;
|
||||||
|
|
||||||
|
static inline void * moduleMatGet(const jerry_call_info_t *callInfo) {
|
||||||
|
return scriptProtoGetValue(&MODULE_MAT4_PROTO, callInfo->this_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatConstructor) {
|
||||||
|
float_t (*ptr)[4] = (float_t (*)[4])memoryAllocate(sizeof(mat4));
|
||||||
|
glm_mat4_identity(ptr);
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
callInfo->this_value, &MODULE_MAT4_PROTO.info, ptr
|
||||||
|
);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatMul) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t (*a)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Mat4.mul: invalid this");
|
||||||
|
float_t (*b)[4] = (float_t (*)[4])scriptProtoGetValue(
|
||||||
|
&MODULE_MAT4_PROTO, args[0]
|
||||||
|
);
|
||||||
|
if(!b) return moduleBaseThrow("Mat4.mul: argument must be a Mat4");
|
||||||
|
mat4 r;
|
||||||
|
glm_mat4_mul(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatTranspose) {
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return moduleBaseThrow("Mat4.transpose: invalid this");
|
||||||
|
mat4 r;
|
||||||
|
glm_mat4_transpose_to(m, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatInverse) {
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return moduleBaseThrow("Mat4.inverse: invalid this");
|
||||||
|
mat4 r;
|
||||||
|
glm_mat4_inv(m, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatDeterminant) {
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return moduleBaseThrow("Mat4.determinant: invalid this");
|
||||||
|
return jerry_number(glm_mat4_det(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatMulVec3) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return moduleBaseThrow("Mat4.mulVec3: invalid this");
|
||||||
|
vec3 vin;
|
||||||
|
if(!moduleVec3Check(args[0], vin)) {
|
||||||
|
return moduleBaseThrow("Mat4.mulVec3: argument must be a Vec3");
|
||||||
|
}
|
||||||
|
float_t w = (argc >= 2 && jerry_value_is_number(args[1]))
|
||||||
|
? (float_t)jerry_value_as_number(args[1]) : 1.0f;
|
||||||
|
vec3 vout;
|
||||||
|
glm_mat4_mulv3(m, vin, w, vout);
|
||||||
|
return moduleVec3Push(vout);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatMulVec4) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return moduleBaseThrow("Mat4.mulVec4: invalid this");
|
||||||
|
vec4 vin;
|
||||||
|
if(!moduleVec4Check(args[0], vin)) {
|
||||||
|
return moduleBaseThrow("Mat4.mulVec4: argument must be a Vec4");
|
||||||
|
}
|
||||||
|
vec4 vout;
|
||||||
|
glm_mat4_mulv(m, vin, vout);
|
||||||
|
return moduleVec4Push(vout);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatTranslate) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return moduleBaseThrow("Mat4.translate: invalid this");
|
||||||
|
vec3 tv;
|
||||||
|
if(!moduleVec3Check(args[0], tv)) {
|
||||||
|
return moduleBaseThrow("Mat4.translate: argument must be a Vec3");
|
||||||
|
}
|
||||||
|
mat4 r;
|
||||||
|
glm_mat4_copy(m, r);
|
||||||
|
glm_translate(r, tv);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatScale) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return moduleBaseThrow("Mat4.scale: invalid this");
|
||||||
|
vec3 sv;
|
||||||
|
if(!moduleVec3Check(args[0], sv)) {
|
||||||
|
return moduleBaseThrow("Mat4.scale: argument must be a Vec3");
|
||||||
|
}
|
||||||
|
mat4 r;
|
||||||
|
glm_mat4_copy(m, r);
|
||||||
|
glm_scale(r, sv);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatStaticIdentity) {
|
||||||
|
mat4 r;
|
||||||
|
glm_mat4_identity(r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatStaticPerspective) {
|
||||||
|
moduleBaseRequireArgs(4);
|
||||||
|
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1);
|
||||||
|
moduleBaseRequireNumber(2); moduleBaseRequireNumber(3);
|
||||||
|
mat4 r;
|
||||||
|
glm_perspective(
|
||||||
|
(float_t)jerry_value_as_number(args[0]),
|
||||||
|
(float_t)jerry_value_as_number(args[1]),
|
||||||
|
(float_t)jerry_value_as_number(args[2]),
|
||||||
|
(float_t)jerry_value_as_number(args[3]),
|
||||||
|
r
|
||||||
|
);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatStaticLookAt) {
|
||||||
|
moduleBaseRequireArgs(3);
|
||||||
|
vec3 eye, center, up;
|
||||||
|
if(!moduleVec3Check(args[0], eye)) {
|
||||||
|
return moduleBaseThrow("Mat4.lookAt: eye must be a Vec3");
|
||||||
|
}
|
||||||
|
if(!moduleVec3Check(args[1], center)) {
|
||||||
|
return moduleBaseThrow("Mat4.lookAt: center must be a Vec3");
|
||||||
|
}
|
||||||
|
if(!moduleVec3Check(args[2], up)) {
|
||||||
|
return moduleBaseThrow("Mat4.lookAt: up must be a Vec3");
|
||||||
|
}
|
||||||
|
mat4 r;
|
||||||
|
glm_lookat(eye, center, up, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMatToString) {
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])moduleMatGet(callInfo);
|
||||||
|
if(!m) return jerry_string_sz("Mat4(?)");
|
||||||
|
char_t buf[256];
|
||||||
|
stringFormat(
|
||||||
|
buf, sizeof(buf),
|
||||||
|
"Mat4([%g,%g,%g,%g], [%g,%g,%g,%g],"
|
||||||
|
" [%g,%g,%g,%g], [%g,%g,%g,%g])",
|
||||||
|
m[0][0], m[0][1], m[0][2], m[0][3],
|
||||||
|
m[1][0], m[1][1], m[1][2], m[1][3],
|
||||||
|
m[2][0], m[2][1], m[2][2], m[2][3],
|
||||||
|
m[3][0], m[3][1], m[3][2], m[3][3]
|
||||||
|
);
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline jerry_value_t moduleMat4Push(float (*m)[4]) {
|
||||||
|
return scriptProtoCreateValue(&MODULE_MAT4_PROTO, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool_t moduleMat4Check(jerry_value_t val, float (*out)[4]) {
|
||||||
|
float_t (*m)[4] = (float_t (*)[4])scriptProtoGetValue(
|
||||||
|
&MODULE_MAT4_PROTO, val
|
||||||
|
);
|
||||||
|
if(!m) return false;
|
||||||
|
glm_mat4_copy(m, out);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleMat4(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_MAT4_PROTO, "Mat4", sizeof(mat4), moduleMatConstructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineToString(&MODULE_MAT4_PROTO, moduleMatToString);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "mul", moduleMatMul
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "transpose", moduleMatTranspose
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "inverse", moduleMatInverse
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "determinant", moduleMatDeterminant
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "mulVec3", moduleMatMulVec3
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "mulVec4", moduleMatMulVec4
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "translate", moduleMatTranslate
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "scale", moduleMatScale
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "identity", moduleMatStaticIdentity
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "perspective", moduleMatStaticPerspective
|
||||||
|
);
|
||||||
|
scriptProtoDefineStaticFunc(
|
||||||
|
&MODULE_MAT4_PROTO, "lookAt", moduleMatStaticLookAt
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "modulevec2.h"
|
||||||
|
#include "modulevec3.h"
|
||||||
|
#include "modulevec3ref.h"
|
||||||
|
#include "modulevec4.h"
|
||||||
|
#include "modulemat4.h"
|
||||||
|
|
||||||
|
static void moduleMath(void) {
|
||||||
|
moduleVec2();
|
||||||
|
moduleVec3();
|
||||||
|
moduleVec3Ref();
|
||||||
|
moduleVec4();
|
||||||
|
moduleMat4();
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "cglm/cglm.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_VEC2_PROTO;
|
||||||
|
|
||||||
|
static inline float_t * moduleVec2Get(const jerry_call_info_t *callInfo) {
|
||||||
|
return (float_t *)scriptProtoGetValue(
|
||||||
|
&MODULE_VEC2_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float_t * moduleVec2From(jerry_value_t val) {
|
||||||
|
return (float_t *)scriptProtoGetValue(&MODULE_VEC2_PROTO, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Constructor) {
|
||||||
|
float_t *ptr = (float_t *)memoryAllocate(sizeof(vec2));
|
||||||
|
ptr[0] = (argc >= 1 && jerry_value_is_number(args[0]))
|
||||||
|
? (float_t)jerry_value_as_number(args[0]) : 0.0f;
|
||||||
|
ptr[1] = (argc >= 2 && jerry_value_is_number(args[1]))
|
||||||
|
? (float_t)jerry_value_as_number(args[1]) : 0.0f;
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
callInfo->this_value, &MODULE_VEC2_PROTO.info, ptr
|
||||||
|
);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2GetX) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
return v ? jerry_number(v[0]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec2SetX) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2GetY) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
return v ? jerry_number(v[1]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec2SetY) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Dot) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec2Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec2.dot: invalid this");
|
||||||
|
float_t *b = moduleVec2From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec2.dot: argument must be a Vec2");
|
||||||
|
return jerry_number(glm_vec2_dot(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Length) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec2.length: invalid this");
|
||||||
|
return jerry_number(glm_vec2_norm(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2LengthSq) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec2.lengthSq: invalid this");
|
||||||
|
return jerry_number(glm_vec2_norm2(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Normalize) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec2.normalize: invalid this");
|
||||||
|
vec2 r;
|
||||||
|
glm_vec2_normalize_to(v, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Negate) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec2.negate: invalid this");
|
||||||
|
vec2 r;
|
||||||
|
glm_vec2_negate_to(v, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Add) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec2Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec2.add: invalid this");
|
||||||
|
float_t *b = moduleVec2From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec2.add: argument must be a Vec2");
|
||||||
|
vec2 r;
|
||||||
|
glm_vec2_add(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Sub) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec2Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec2.sub: invalid this");
|
||||||
|
float_t *b = moduleVec2From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec2.sub: argument must be a Vec2");
|
||||||
|
vec2 r;
|
||||||
|
glm_vec2_sub(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Scale) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec2.scale: invalid this");
|
||||||
|
vec2 r;
|
||||||
|
glm_vec2_scale(v, (float_t)jerry_value_as_number(args[0]), r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Lerp) {
|
||||||
|
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
|
||||||
|
float_t *a = moduleVec2Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec2.lerp: invalid this");
|
||||||
|
float_t *b = moduleVec2From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec2.lerp: first argument must be a Vec2");
|
||||||
|
vec2 r;
|
||||||
|
glm_vec2_lerp(a, b, (float_t)jerry_value_as_number(args[1]), r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2Distance) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec2Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec2.distance: invalid this");
|
||||||
|
float_t *b = moduleVec2From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec2.distance: argument must be a Vec2");
|
||||||
|
return jerry_number(glm_vec2_distance(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec2ToString) {
|
||||||
|
float_t *v = moduleVec2Get(callInfo);
|
||||||
|
if(!v) return jerry_string_sz("Vec2(?, ?)");
|
||||||
|
char_t buf[64];
|
||||||
|
stringFormat(buf, sizeof(buf), "Vec2(%g, %g)", v[0], v[1]);
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline jerry_value_t moduleVec2Push(const float_t *v) {
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC2_PROTO, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool_t moduleVec2Check(jerry_value_t val, float_t *out) {
|
||||||
|
float_t *v = moduleVec2From(val);
|
||||||
|
if(!v) return false;
|
||||||
|
out[0] = v[0];
|
||||||
|
out[1] = v[1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleVec2(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_VEC2_PROTO, "Vec2", sizeof(vec2), moduleVec2Constructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC2_PROTO, "x", moduleVec2GetX, moduleVec2SetX
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC2_PROTO, "y", moduleVec2GetY, moduleVec2SetY
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineToString(&MODULE_VEC2_PROTO, moduleVec2ToString);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "dot", moduleVec2Dot);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "length", moduleVec2Length);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "lengthSq", moduleVec2LengthSq);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "normalize", moduleVec2Normalize);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "negate", moduleVec2Negate);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "add", moduleVec2Add);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "sub", moduleVec2Sub);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "scale", moduleVec2Scale);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "lerp", moduleVec2Lerp);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC2_PROTO, "distance", moduleVec2Distance);
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "cglm/cglm.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_VEC3_PROTO;
|
||||||
|
|
||||||
|
static inline float_t * moduleVec3Get(const jerry_call_info_t *callInfo) {
|
||||||
|
return (float_t *)scriptProtoGetValue(
|
||||||
|
&MODULE_VEC3_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float_t * moduleVec3From(jerry_value_t val) {
|
||||||
|
return (float_t *)scriptProtoGetValue(&MODULE_VEC3_PROTO, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Constructor) {
|
||||||
|
float_t *ptr = (float_t *)memoryAllocate(sizeof(vec3));
|
||||||
|
ptr[0] = (argc >= 1 && jerry_value_is_number(args[0]))
|
||||||
|
? (float_t)jerry_value_as_number(args[0]) : 0.0f;
|
||||||
|
ptr[1] = (argc >= 2 && jerry_value_is_number(args[1]))
|
||||||
|
? (float_t)jerry_value_as_number(args[1]) : 0.0f;
|
||||||
|
ptr[2] = (argc >= 3 && jerry_value_is_number(args[2]))
|
||||||
|
? (float_t)jerry_value_as_number(args[2]) : 0.0f;
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
callInfo->this_value, &MODULE_VEC3_PROTO.info, ptr
|
||||||
|
);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3GetX) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
return v ? jerry_number(v[0]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec3SetX) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3GetY) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
return v ? jerry_number(v[1]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec3SetY) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3GetZ) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
return v ? jerry_number(v[2]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec3SetZ) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(v && argc > 0) v[2] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Dot) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec3Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec3.dot: invalid this");
|
||||||
|
float_t *b = moduleVec3From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec3.dot: argument must be a Vec3");
|
||||||
|
return jerry_number(glm_vec3_dot(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Cross) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec3Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec3.cross: invalid this");
|
||||||
|
float_t *b = moduleVec3From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec3.cross: argument must be a Vec3");
|
||||||
|
vec3 r;
|
||||||
|
glm_vec3_cross(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Length) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec3.length: invalid this");
|
||||||
|
return jerry_number(glm_vec3_norm(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3LengthSq) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec3.lengthSq: invalid this");
|
||||||
|
return jerry_number(glm_vec3_norm2(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Normalize) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec3.normalize: invalid this");
|
||||||
|
vec3 r;
|
||||||
|
glm_vec3_normalize_to(v, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Negate) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec3.negate: invalid this");
|
||||||
|
vec3 r;
|
||||||
|
glm_vec3_negate_to(v, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Add) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec3Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec3.add: invalid this");
|
||||||
|
float_t *b = moduleVec3From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec3.add: argument must be a Vec3");
|
||||||
|
vec3 r;
|
||||||
|
glm_vec3_add(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Sub) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec3Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec3.sub: invalid this");
|
||||||
|
float_t *b = moduleVec3From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec3.sub: argument must be a Vec3");
|
||||||
|
vec3 r;
|
||||||
|
glm_vec3_sub(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Scale) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec3.scale: invalid this");
|
||||||
|
vec3 r;
|
||||||
|
glm_vec3_scale(v, (float_t)jerry_value_as_number(args[0]), r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Lerp) {
|
||||||
|
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
|
||||||
|
float_t *a = moduleVec3Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec3.lerp: invalid this");
|
||||||
|
float_t *b = moduleVec3From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec3.lerp: first argument must be a Vec3");
|
||||||
|
vec3 r;
|
||||||
|
glm_vec3_lerp(a, b, (float_t)jerry_value_as_number(args[1]), r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3Distance) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec3Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec3.distance: invalid this");
|
||||||
|
float_t *b = moduleVec3From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec3.distance: argument must be a Vec3");
|
||||||
|
return jerry_number(glm_vec3_distance(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3ToString) {
|
||||||
|
float_t *v = moduleVec3Get(callInfo);
|
||||||
|
if(!v) return jerry_string_sz("Vec3(?, ?, ?)");
|
||||||
|
char_t buf[80];
|
||||||
|
stringFormat(buf, sizeof(buf), "Vec3(%g, %g, %g)", v[0], v[1], v[2]);
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline jerry_value_t moduleVec3Push(const float_t *v) {
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_PROTO, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool_t moduleVec3Check(jerry_value_t val, float_t *out) {
|
||||||
|
float_t *v = moduleVec3From(val);
|
||||||
|
if(!v) return false;
|
||||||
|
out[0] = v[0];
|
||||||
|
out[1] = v[1];
|
||||||
|
out[2] = v[2];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleVec3(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_VEC3_PROTO, "Vec3", sizeof(vec3), moduleVec3Constructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC3_PROTO, "x", moduleVec3GetX, moduleVec3SetX
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC3_PROTO, "y", moduleVec3GetY, moduleVec3SetY
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC3_PROTO, "z", moduleVec3GetZ, moduleVec3SetZ
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineToString(&MODULE_VEC3_PROTO, moduleVec3ToString);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "dot", moduleVec3Dot);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "cross", moduleVec3Cross);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "length", moduleVec3Length);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "lengthSq", moduleVec3LengthSq);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "normalize", moduleVec3Normalize);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "negate", moduleVec3Negate);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "add", moduleVec3Add);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "sub", moduleVec3Sub);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "scale", moduleVec3Scale);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "lerp", moduleVec3Lerp);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC3_PROTO, "distance", moduleVec3Distance);
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "cglm/cglm.h"
|
||||||
|
#include "modulevec3.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float_t *data;
|
||||||
|
void (*onChange)(void *ctx);
|
||||||
|
void *ctx;
|
||||||
|
} vec3ref_t;
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_VEC3_REF_PROTO;
|
||||||
|
|
||||||
|
static inline vec3ref_t * moduleVec3RefGet(
|
||||||
|
const jerry_call_info_t *callInfo
|
||||||
|
) {
|
||||||
|
return (vec3ref_t*)scriptProtoGetValue(
|
||||||
|
&MODULE_VEC3_REF_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vec3ref_t * moduleVec3RefFrom(jerry_value_t val) {
|
||||||
|
return (vec3ref_t*)scriptProtoGetValue(&MODULE_VEC3_REF_PROTO, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void moduleVec3RefNotify(vec3ref_t *ref) {
|
||||||
|
if(ref->onChange) ref->onChange(ref->ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3RefGetX) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefGet(callInfo);
|
||||||
|
return ref ? jerry_number(ref->data[0]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec3RefSetX) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefGet(callInfo);
|
||||||
|
if(ref && argc > 0) {
|
||||||
|
ref->data[0] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
moduleVec3RefNotify(ref);
|
||||||
|
}
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3RefGetY) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefGet(callInfo);
|
||||||
|
return ref ? jerry_number(ref->data[1]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec3RefSetY) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefGet(callInfo);
|
||||||
|
if(ref && argc > 0) {
|
||||||
|
ref->data[1] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
moduleVec3RefNotify(ref);
|
||||||
|
}
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3RefGetZ) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefGet(callInfo);
|
||||||
|
return ref ? jerry_number(ref->data[2]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec3RefSetZ) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefGet(callInfo);
|
||||||
|
if(ref && argc > 0) {
|
||||||
|
ref->data[2] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
moduleVec3RefNotify(ref);
|
||||||
|
}
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec3RefToString) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefGet(callInfo);
|
||||||
|
if(!ref) return jerry_string_sz("Vec3(?, ?, ?)");
|
||||||
|
char_t buf[80];
|
||||||
|
stringFormat(
|
||||||
|
buf, sizeof(buf),
|
||||||
|
"Vec3(%g, %g, %g)", ref->data[0], ref->data[1], ref->data[2]
|
||||||
|
);
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline jerry_value_t moduleVec3RefPush(
|
||||||
|
float_t *data, void (*onChange)(void *), void *ctx
|
||||||
|
) {
|
||||||
|
vec3ref_t ref = { .data = data, .onChange = onChange, .ctx = ctx };
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC3_REF_PROTO, &ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool_t moduleVec3RefCheck(jerry_value_t val, float_t *out) {
|
||||||
|
vec3ref_t *ref = moduleVec3RefFrom(val);
|
||||||
|
if(!ref) return false;
|
||||||
|
out[0] = ref->data[0];
|
||||||
|
out[1] = ref->data[1];
|
||||||
|
out[2] = ref->data[2];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool_t moduleVec3AnyCheck(jerry_value_t val, float_t *out) {
|
||||||
|
return moduleVec3Check(val, out) || moduleVec3RefCheck(val, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleVec3Ref(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_VEC3_REF_PROTO, NULL, sizeof(vec3ref_t), NULL
|
||||||
|
);
|
||||||
|
scriptProtoDefineToString(&MODULE_VEC3_REF_PROTO, moduleVec3RefToString);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC3_REF_PROTO, "x",
|
||||||
|
moduleVec3RefGetX, moduleVec3RefSetX
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC3_REF_PROTO, "y",
|
||||||
|
moduleVec3RefGetY, moduleVec3RefSetY
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC3_REF_PROTO, "z",
|
||||||
|
moduleVec3RefGetZ, moduleVec3RefSetZ
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "cglm/cglm.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_VEC4_PROTO;
|
||||||
|
|
||||||
|
static inline float_t * moduleVec4Get(const jerry_call_info_t *callInfo) {
|
||||||
|
return (float_t *)scriptProtoGetValue(
|
||||||
|
&MODULE_VEC4_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float_t * moduleVec4From(jerry_value_t val) {
|
||||||
|
return (float_t *)scriptProtoGetValue(&MODULE_VEC4_PROTO, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Constructor) {
|
||||||
|
float_t *ptr = (float_t *)memoryAllocate(sizeof(vec4));
|
||||||
|
ptr[0] = (argc >= 1 && jerry_value_is_number(args[0]))
|
||||||
|
? (float_t)jerry_value_as_number(args[0]) : 0.0f;
|
||||||
|
ptr[1] = (argc >= 2 && jerry_value_is_number(args[1]))
|
||||||
|
? (float_t)jerry_value_as_number(args[1]) : 0.0f;
|
||||||
|
ptr[2] = (argc >= 3 && jerry_value_is_number(args[2]))
|
||||||
|
? (float_t)jerry_value_as_number(args[2]) : 0.0f;
|
||||||
|
ptr[3] = (argc >= 4 && jerry_value_is_number(args[3]))
|
||||||
|
? (float_t)jerry_value_as_number(args[3]) : 0.0f;
|
||||||
|
jerry_object_set_native_ptr(
|
||||||
|
callInfo->this_value, &MODULE_VEC4_PROTO.info, ptr
|
||||||
|
);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
// x/y/z/w
|
||||||
|
moduleBaseFunction(moduleVec4GetX) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[0]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetX) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4GetY) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[1]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetY) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4GetZ) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[2]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetZ) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[2] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4GetW) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[3]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetW) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[3] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
// u0/v0/u1/v1 aliases for UV coordinates
|
||||||
|
moduleBaseFunction(moduleVec4GetU0) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[0]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetU0) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[0] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4GetV0) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[1]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetV0) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[1] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4GetU1) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[2]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetU1) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[2] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4GetV1) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
return v ? jerry_number(v[3]) : jerry_undefined();
|
||||||
|
}
|
||||||
|
moduleBaseFunction(moduleVec4SetV1) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(v && argc > 0) v[3] = (float_t)jerry_value_as_number(args[0]);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Dot) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec4Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec4.dot: invalid this");
|
||||||
|
float_t *b = moduleVec4From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec4.dot: argument must be a Vec4");
|
||||||
|
return jerry_number(glm_vec4_dot(a, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Length) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec4.length: invalid this");
|
||||||
|
return jerry_number(glm_vec4_norm(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4LengthSq) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec4.lengthSq: invalid this");
|
||||||
|
return jerry_number(glm_vec4_norm2(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Normalize) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec4.normalize: invalid this");
|
||||||
|
vec4 r;
|
||||||
|
glm_vec4_normalize_to(v, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Negate) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec4.negate: invalid this");
|
||||||
|
vec4 r;
|
||||||
|
glm_vec4_negate_to(v, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Add) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec4Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec4.add: invalid this");
|
||||||
|
float_t *b = moduleVec4From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec4.add: argument must be a Vec4");
|
||||||
|
vec4 r;
|
||||||
|
glm_vec4_add(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Sub) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
float_t *a = moduleVec4Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec4.sub: invalid this");
|
||||||
|
float_t *b = moduleVec4From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec4.sub: argument must be a Vec4");
|
||||||
|
vec4 r;
|
||||||
|
glm_vec4_sub(a, b, r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Scale) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireNumber(0);
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(!v) return moduleBaseThrow("Vec4.scale: invalid this");
|
||||||
|
vec4 r;
|
||||||
|
glm_vec4_scale(v, (float_t)jerry_value_as_number(args[0]), r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4Lerp) {
|
||||||
|
moduleBaseRequireArgs(2); moduleBaseRequireNumber(1);
|
||||||
|
float_t *a = moduleVec4Get(callInfo);
|
||||||
|
if(!a) return moduleBaseThrow("Vec4.lerp: invalid this");
|
||||||
|
float_t *b = moduleVec4From(args[0]);
|
||||||
|
if(!b) return moduleBaseThrow("Vec4.lerp: first argument must be a Vec4");
|
||||||
|
vec4 r;
|
||||||
|
glm_vec4_lerp(a, b, (float_t)jerry_value_as_number(args[1]), r);
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleVec4ToString) {
|
||||||
|
float_t *v = moduleVec4Get(callInfo);
|
||||||
|
if(!v) return jerry_string_sz("Vec4(?, ?, ?, ?)");
|
||||||
|
char_t buf[96];
|
||||||
|
stringFormat(
|
||||||
|
buf, sizeof(buf),
|
||||||
|
"Vec4(%g, %g, %g, %g)", v[0], v[1], v[2], v[3]
|
||||||
|
);
|
||||||
|
return jerry_string_sz(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline jerry_value_t moduleVec4Push(const float_t *v) {
|
||||||
|
return scriptProtoCreateValue(&MODULE_VEC4_PROTO, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool_t moduleVec4Check(jerry_value_t val, float_t *out) {
|
||||||
|
float_t *v = moduleVec4From(val);
|
||||||
|
if(!v) return false;
|
||||||
|
out[0] = v[0];
|
||||||
|
out[1] = v[1];
|
||||||
|
out[2] = v[2];
|
||||||
|
out[3] = v[3];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleVec4(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_VEC4_PROTO, "Vec4", sizeof(vec4), moduleVec4Constructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "x", moduleVec4GetX, moduleVec4SetX
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "y", moduleVec4GetY, moduleVec4SetY
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "z", moduleVec4GetZ, moduleVec4SetZ
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "w", moduleVec4GetW, moduleVec4SetW
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "u0", moduleVec4GetU0, moduleVec4SetU0
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "v0", moduleVec4GetV0, moduleVec4SetV0
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "u1", moduleVec4GetU1, moduleVec4SetU1
|
||||||
|
);
|
||||||
|
scriptProtoDefineProp(
|
||||||
|
&MODULE_VEC4_PROTO, "v1", moduleVec4GetV1, moduleVec4SetV1
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineToString(&MODULE_VEC4_PROTO, moduleVec4ToString);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "dot", moduleVec4Dot);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "length", moduleVec4Length);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "lengthSq", moduleVec4LengthSq);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "normalize", moduleVec4Normalize);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "negate", moduleVec4Negate);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "add", moduleVec4Add);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "sub", moduleVec4Sub);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "scale", moduleVec4Scale);
|
||||||
|
scriptProtoDefineFunc(&MODULE_VEC4_PROTO, "lerp", moduleVec4Lerp);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/script/moduleinclude.h"
|
||||||
|
#include "script/module/math/modulemath.h"
|
||||||
|
#include "script/module/entity/moduleentity.h"
|
||||||
|
#include "script/module/input/moduleinput.h"
|
||||||
|
#include "script/module/moduleplatform.h"
|
||||||
|
#include "script/module/time/moduletime.h"
|
||||||
|
#include "script/module/display/modulecolor.h"
|
||||||
|
#include "script/module/display/modulescreen.h"
|
||||||
|
#include "script/module/scene/modulescene.h"
|
||||||
|
#include "script/module/console/moduleconsole.h"
|
||||||
|
#include "script/module/engine/moduleengine.h"
|
||||||
|
|
||||||
|
static void moduleRegister(void) {
|
||||||
|
moduleInclude();
|
||||||
|
moduleMath();
|
||||||
|
moduleEntity();
|
||||||
|
moduleInput();
|
||||||
|
modulePlatform();
|
||||||
|
moduleTime();
|
||||||
|
moduleColor();
|
||||||
|
moduleScreen();
|
||||||
|
moduleScene();
|
||||||
|
moduleConsole();
|
||||||
|
moduleEngine();
|
||||||
|
}
|
||||||
@@ -0,0 +1,425 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/scriptmanager.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a function for a module in JavaScript.
|
||||||
|
*
|
||||||
|
* @param name Name of the method (in C, not JS)
|
||||||
|
* @return A C function with that name, containing the standard JS sig.
|
||||||
|
*/
|
||||||
|
#define moduleBaseFunction(name) \
|
||||||
|
static jerry_value_t name( \
|
||||||
|
const jerry_call_info_t *callInfo, \
|
||||||
|
const jerry_value_t args[], \
|
||||||
|
const jerry_length_t argc)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a standard JS prototype for a module.
|
||||||
|
* This creates the INFO struct, and a prototype reference value.
|
||||||
|
*
|
||||||
|
* Values are;
|
||||||
|
* {NAME}_INFO and {NAME_PROTOTYPE}
|
||||||
|
*
|
||||||
|
* @param name Name of the module.
|
||||||
|
* @return See above.
|
||||||
|
*/
|
||||||
|
#define moduleBaseProtoDefine(name) \
|
||||||
|
static const jerry_object_native_info_t name##_INFO = { \
|
||||||
|
.free_cb = moduleBaseFreeProto, \
|
||||||
|
.number_of_references = 0, \
|
||||||
|
.offset_of_references = 0 \
|
||||||
|
}; \
|
||||||
|
static jerry_value_t name##_PROTOTYPE = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the pointer to what is otherwise a prototype in the JerryScript code.
|
||||||
|
* This, for example, allows you to define a pointer in C and have it be a
|
||||||
|
* prototype for objects in JavaScript.
|
||||||
|
*
|
||||||
|
* @param object The JavaScript object to get the native pointer from.
|
||||||
|
* @param info The native info "prototype" struct used to get the pointer.
|
||||||
|
* @return The pointer to the proto (or NULL).
|
||||||
|
*/
|
||||||
|
static void* moduleBaseGetProto(
|
||||||
|
const jerry_value_t object,
|
||||||
|
const jerry_object_native_info_t *info
|
||||||
|
) {
|
||||||
|
assertNotNull(info, "Native info must not be null");
|
||||||
|
if(!jerry_value_is_object(object)) return NULL;
|
||||||
|
|
||||||
|
return (void*)jerry_object_get_native_ptr(object, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an object based on a C value.
|
||||||
|
*
|
||||||
|
* @param input The pointer to the value to create a JS object for.
|
||||||
|
* @param size The size of the data provided in input.
|
||||||
|
* @param info The native info "prototype" struct used to create the object.
|
||||||
|
* @return A JS object wrapping the provided C value.
|
||||||
|
*/
|
||||||
|
static jerry_value_t moduleBaseCreateProto(
|
||||||
|
void *input,
|
||||||
|
const size_t size,
|
||||||
|
const jerry_object_native_info_t *info,
|
||||||
|
const jerry_value_t prototype
|
||||||
|
) {
|
||||||
|
assertNotNull(input, "Input pointer must not be null");
|
||||||
|
assertTrue(size > 0, "Struct size must be greater than 0");
|
||||||
|
assertNotNull(info, "Native info must not be null");
|
||||||
|
|
||||||
|
void *ptr = memoryAllocate(size);
|
||||||
|
memoryCopy(ptr, input, size);
|
||||||
|
jerry_value_t proto = jerry_object();
|
||||||
|
jerry_object_set_native_ptr(proto, info, ptr);
|
||||||
|
jerry_object_set_proto(proto, prototype);
|
||||||
|
return proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard JerryScript free callback.
|
||||||
|
*
|
||||||
|
* @param ptr The pointer to free.
|
||||||
|
* @param info The native info struct associated with the pointer.
|
||||||
|
*/
|
||||||
|
static void moduleBaseFreeProto(void *ptr, jerry_object_native_info_t *info) {
|
||||||
|
assertNotNull(ptr, "Pointer must not be null");
|
||||||
|
assertNotNull(info, "Native info must not be null");
|
||||||
|
memoryFree(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quickly defines a property on a prototype with a getter and setter.
|
||||||
|
*/
|
||||||
|
static void moduleBaseProtoDefineProp(
|
||||||
|
const jerry_value_t prototype,
|
||||||
|
const char_t *name,
|
||||||
|
jerry_external_handler_t getter,
|
||||||
|
jerry_external_handler_t setter
|
||||||
|
) {
|
||||||
|
assertTrue(prototype != 0, "Prototype must not be null");
|
||||||
|
assertNotNull(name, "Property name must not be null");
|
||||||
|
assertNotNull(getter, "Getter must not be null");
|
||||||
|
|
||||||
|
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(prototype, key, &desc);
|
||||||
|
jerry_value_free(result);
|
||||||
|
jerry_value_free(key);
|
||||||
|
jerry_value_free(desc.getter);
|
||||||
|
if(setter != NULL) jerry_value_free(desc.setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a global function for a module.
|
||||||
|
*
|
||||||
|
* @param name The name of the function as seen in JavaScript.
|
||||||
|
* @param fn The C handler function for the method.
|
||||||
|
*/
|
||||||
|
static void moduleBaseFunctionRegister(
|
||||||
|
const char_t *name,
|
||||||
|
jerry_external_handler_t fn
|
||||||
|
) {
|
||||||
|
assertNotNull(name, "Function name must not be null");
|
||||||
|
assertNotNull(fn, "Function handler must not be null");
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates a script in the global scope.
|
||||||
|
*
|
||||||
|
* @param script The script to evaluate.
|
||||||
|
*/
|
||||||
|
static void moduleBaseEval(const char_t *script) {
|
||||||
|
assertNotNull(script, "Script must not be null");
|
||||||
|
|
||||||
|
jerry_value_t result = jerry_eval(
|
||||||
|
(const jerry_char_t *)script,
|
||||||
|
strlen(script),
|
||||||
|
JERRY_PARSE_NO_OPTS
|
||||||
|
);
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw a type error from a module function.
|
||||||
|
*
|
||||||
|
* @param message The error message to throw.
|
||||||
|
* @return A JerryScript error value.
|
||||||
|
*/
|
||||||
|
static jerry_value_t moduleBaseThrow(const char_t *message) {
|
||||||
|
assertStrLenMin(message, 1, "Error message must not be empty");
|
||||||
|
return jerry_throw_sz(JERRY_ERROR_TYPE, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a global string constant.
|
||||||
|
*/
|
||||||
|
static void moduleBaseSetString(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a global object with the provided name and prototype.
|
||||||
|
*
|
||||||
|
* @param name The name of the global object to create.
|
||||||
|
* @param prototype The prototype to set on the global object.
|
||||||
|
*/
|
||||||
|
static void moduleBaseCreateGlobalObject(
|
||||||
|
const char_t *name, jerry_value_t prototype
|
||||||
|
) {
|
||||||
|
jerry_value_t global = jerry_current_realm();
|
||||||
|
jerry_value_t key = jerry_string_sz(name);
|
||||||
|
jerry_object_set(global, key, prototype);
|
||||||
|
jerry_value_free(key);
|
||||||
|
jerry_value_free(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert at least n arguments were passed; return type error if not.
|
||||||
|
*/
|
||||||
|
#define moduleBaseRequireArgs(n) \
|
||||||
|
if((argc) < (n)) { \
|
||||||
|
return moduleBaseThrow("Expected at least " #n " arguments"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert an argument is a number; return type error if not.
|
||||||
|
*/
|
||||||
|
#define moduleBaseRequireNumber(i) do { \
|
||||||
|
if(!jerry_value_is_number(args[(i)])) { \
|
||||||
|
return moduleBaseThrow("Expected number argument"); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert an argument is a string; return type error if not.
|
||||||
|
*/
|
||||||
|
#define moduleBaseRequireString(i) do { \
|
||||||
|
if(!jerry_value_is_string(args[(i)])) { \
|
||||||
|
return moduleBaseThrow("Expected string argument"); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert an argument is a function; return type error if not.
|
||||||
|
*/
|
||||||
|
#define moduleBaseRequireFunction(i) do { \
|
||||||
|
if(!jerry_value_is_function(args[(i)])) { \
|
||||||
|
return moduleBaseThrow("Expected function argument"); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert an argument is an object; return type error if not.
|
||||||
|
*/
|
||||||
|
#define moduleBaseRequireObject(i) do { \
|
||||||
|
if(!jerry_value_is_object(args[(i)])) { \
|
||||||
|
return moduleBaseThrow("Expected object argument"); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a global numeric constant.
|
||||||
|
*/
|
||||||
|
static inline void moduleBaseSetNumber(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 moduleBaseSetInt(const char_t *name, int32_t value) {
|
||||||
|
moduleBaseSetNumber(name, (double)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a global JS value. Caller retains ownership of the value and must free
|
||||||
|
* it independently.
|
||||||
|
*/
|
||||||
|
static inline void moduleBaseSetValue(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 moduleBaseWrapPointer(void *ptr) {
|
||||||
|
jerry_value_t obj = jerry_object();
|
||||||
|
jerry_object_set_native_ptr(obj, &JS_PTR_NATIVE_INFO, ptr);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a named global to a wrapped engine-owned C pointer.
|
||||||
|
* Combines moduleBaseWrapPointer and moduleBaseSetValue in one call.
|
||||||
|
*/
|
||||||
|
static inline void moduleBaseSetWrappedPointer(const char_t *name, void *ptr) {
|
||||||
|
jerry_value_t val = moduleBaseWrapPointer(ptr);
|
||||||
|
moduleBaseSetValue(name, val);
|
||||||
|
jerry_value_free(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unwrap a C pointer from a JS object created by moduleBaseWrapPointer.
|
||||||
|
* Returns NULL if the object does not carry a matching native pointer.
|
||||||
|
*/
|
||||||
|
static inline void *moduleBaseUnwrapPointer(jerry_value_t val) {
|
||||||
|
if(!jerry_value_is_object(val)) return NULL;
|
||||||
|
return jerry_object_get_native_ptr(val, &JS_PTR_NATIVE_INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 moduleBaseToString(
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @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 moduleBaseDefineProperty(
|
||||||
|
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 moduleBaseDefineMethod(
|
||||||
|
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 moduleBaseExceptionMessage(
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a named global function.
|
||||||
|
*
|
||||||
|
* @param name The name of the function as seen in JavaScript.
|
||||||
|
* @param fn The C handler function for the method.
|
||||||
|
*/
|
||||||
|
static inline void moduleBaseDefineGlobalMethod(
|
||||||
|
const char_t *name,
|
||||||
|
jerry_external_handler_t fn
|
||||||
|
) {
|
||||||
|
jerry_value_t global = jerry_current_realm();
|
||||||
|
moduleBaseDefineMethod(global, name, fn);
|
||||||
|
jerry_value_free(global);
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
|
||||||
void modulePlatform(scriptcontext_t *ctx) {
|
static void modulePlatform(void) {
|
||||||
assertNotNull(ctx, "Script context cannot be NULL");
|
moduleBaseEval(MODULE_PLATFORM_VALUE);
|
||||||
|
|
||||||
scriptContextExec(ctx, MODULE_PLATFORM_VALUE);
|
|
||||||
|
|
||||||
#ifdef modulePlatformPlatform
|
#ifdef modulePlatformPlatform
|
||||||
modulePlatformPlatform(ctx);
|
modulePlatformPlatform();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2026 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "modulescene.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "scene/scene.h"
|
|
||||||
|
|
||||||
void moduleScene(scriptcontext_t *ctx) {
|
|
||||||
assertNotNull(ctx, "Script context cannot be NULL");
|
|
||||||
|
|
||||||
lua_register(ctx->luaState, "sceneSet", moduleSceneSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
int moduleSceneSet(lua_State *L) {
|
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
|
||||||
|
|
||||||
// Need string
|
|
||||||
if(!lua_isstring(L, 1)) {
|
|
||||||
luaL_error(L, "sceneSet requires a string argument");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char_t *script = lua_tostring(L, 1);
|
|
||||||
|
|
||||||
errorret_t err = sceneSet(script);
|
|
||||||
if(err.code != ERROR_OK) {
|
|
||||||
errorCatch(errorPrint(err));
|
|
||||||
luaL_error(L, "Failed to set scene");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,24 +1,110 @@
|
|||||||
/**
|
/**
|
||||||
* 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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "script/scriptcontext.h"
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "scene/scene.h"
|
||||||
|
|
||||||
/**
|
static scriptproto_t MODULE_SCENE_PROTO;
|
||||||
* Register the scene module within the given script context.
|
|
||||||
*
|
|
||||||
* @param ctx The script context to register the module in.
|
|
||||||
*/
|
|
||||||
void moduleScene(scriptcontext_t *ctx);
|
|
||||||
|
|
||||||
/**
|
moduleBaseFunction(moduleSceneDefaultUpdate) {
|
||||||
* Lua binding for sceneSet function.
|
return jerry_undefined();
|
||||||
*
|
}
|
||||||
* @param L The Lua state.
|
|
||||||
* @return int Number of return values on the Lua stack.
|
moduleBaseFunction(moduleSceneDefaultDispose) {
|
||||||
*/
|
return jerry_undefined();
|
||||||
int moduleSceneSet(lua_State *L);
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleSceneDefaultConstructor) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleSceneSet) {
|
||||||
|
moduleBaseRequireArgs(1);
|
||||||
|
moduleBaseRequireString(0);
|
||||||
|
|
||||||
|
char_t name[ASSET_FILE_PATH_MAX];
|
||||||
|
moduleBaseToString(args[0], name, sizeof(name));
|
||||||
|
if(name[0] == '\0') return moduleBaseThrow("Scene.set: name cannot be empty");
|
||||||
|
|
||||||
|
sceneSet(name);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleSceneGetCurrent) {
|
||||||
|
if(SCENE.sceneCurrent[0] == '\0') return jerry_undefined();
|
||||||
|
return jerry_string_sz(SCENE.sceneCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleSceneReset(void) {
|
||||||
|
if(SCENE.scriptRef != SCENE_SCRIPT_REF_NONE) {
|
||||||
|
jerry_value_free(SCENE.scriptRef);
|
||||||
|
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop the 'module' global reference to the scene class so JerryScript's
|
||||||
|
// GC can collect it and all associated closures.
|
||||||
|
jerry_value_t global = jerry_current_realm();
|
||||||
|
jerry_value_t key = jerry_string_sz("module");
|
||||||
|
jerry_value_t undef = jerry_undefined();
|
||||||
|
jerry_object_set(global, key, undef);
|
||||||
|
jerry_value_free(undef);
|
||||||
|
jerry_value_free(key);
|
||||||
|
jerry_value_free(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
static errorret_t moduleSceneCall(const char_t *method) {
|
||||||
|
assertStrLenMin(method, 1, "Method name cannot be empty");
|
||||||
|
|
||||||
|
if(SCENE.scriptRef == SCENE_SCRIPT_REF_NONE) {
|
||||||
|
errorThrow("No active scene script to call method on");
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz(method);
|
||||||
|
jerry_value_t fn = jerry_object_get(SCENE.scriptRef, key);
|
||||||
|
jerry_value_free(key);
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(fn)) {
|
||||||
|
jerry_value_free(fn);
|
||||||
|
errorThrow("Scene method '%s' not found", method);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t result = jerry_call(fn, SCENE.scriptRef, NULL, 0);
|
||||||
|
jerry_value_free(fn);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(result)) {
|
||||||
|
char_t errMsg[512];
|
||||||
|
moduleBaseExceptionMessage(result, errMsg, sizeof(errMsg));
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorThrow("Scene:%s failed: %s", method, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleScene(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_SCENE_PROTO,
|
||||||
|
"Scene",
|
||||||
|
sizeof(uint8_t),
|
||||||
|
moduleSceneDefaultConstructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_SCENE_PROTO, "update", moduleSceneDefaultUpdate
|
||||||
|
);
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_SCENE_PROTO, "dispose", moduleSceneDefaultDispose
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(&MODULE_SCENE_PROTO, "set", moduleSceneSet);
|
||||||
|
scriptProtoDefineStaticProp(
|
||||||
|
&MODULE_SCENE_PROTO, "current", moduleSceneGetCurrent, NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleIncludeInclude) {
|
||||||
|
if(argc < 1 || !jerry_value_is_string(args[0])) {
|
||||||
|
return moduleBaseThrow("Expected string filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
char_t filename[1024];
|
||||||
|
moduleBaseToString(args[0], filename, sizeof(filename));
|
||||||
|
|
||||||
|
if(filename[0] == '\0') {
|
||||||
|
return moduleBaseThrow("Filename cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(filename);
|
||||||
|
if(len < 3 || stringCompare(&filename[len - 3], ".js") != 0) {
|
||||||
|
return moduleBaseThrow("include: filename must end with .js");
|
||||||
|
}
|
||||||
|
|
||||||
|
char_t buffer[1024];
|
||||||
|
stringCopy(buffer, filename, sizeof(buffer));
|
||||||
|
|
||||||
|
// Save and reset 'export' so the included module gets a clean undefined
|
||||||
|
// default. Saving lets nested includes each have their own export scope.
|
||||||
|
jerry_value_t global = jerry_current_realm();
|
||||||
|
jerry_value_t moduleKey = jerry_string_sz("module");
|
||||||
|
jerry_value_t prevModule = jerry_object_get(global, moduleKey);
|
||||||
|
jerry_value_t undef = jerry_undefined();
|
||||||
|
jerry_object_set(global, moduleKey, undef);
|
||||||
|
jerry_value_free(undef);
|
||||||
|
|
||||||
|
jerry_value_t result = 0;
|
||||||
|
errorret_t err = scriptManagerExecFile(buffer, &result);
|
||||||
|
if(result != 0) jerry_value_free(result);
|
||||||
|
|
||||||
|
// Capture whatever the module assigned to 'module', then restore the
|
||||||
|
// caller's value so nested includes don't clobber each other.
|
||||||
|
jerry_value_t moduleVal = jerry_object_get(global, moduleKey);
|
||||||
|
jerry_object_set(global, moduleKey, prevModule);
|
||||||
|
jerry_value_free(prevModule);
|
||||||
|
jerry_value_free(moduleKey);
|
||||||
|
jerry_value_free(global);
|
||||||
|
|
||||||
|
if(err.code != ERROR_OK) {
|
||||||
|
jerry_value_free(moduleVal);
|
||||||
|
errorCatch(errorPrint(err));
|
||||||
|
return moduleBaseThrow("Failed to include script file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return moduleVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleInclude(void) {
|
||||||
|
moduleBaseDefineGlobalMethod("include", moduleIncludeInclude);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user