Compare commits
2 Commits
playertest
..
module
| Author | SHA1 | Date | |
|---|---|---|---|
| ca6cbf41c1 | |||
| 2a9667feca |
@@ -53,22 +53,6 @@ jobs:
|
||||
path: ./git-artifcats/Dusk
|
||||
if-no-files-found: error
|
||||
|
||||
# build-vita:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Checkout repository
|
||||
# uses: actions/checkout@v6
|
||||
# - name: Set up Docker
|
||||
# uses: docker/setup-docker-action@v5
|
||||
# - name: Build Vita
|
||||
# run: ./scripts/build-vita-docker.sh
|
||||
# - name: Upload Vita binary
|
||||
# uses: actions/upload-artifact@v6
|
||||
# with:
|
||||
# name: dusk-vita
|
||||
# path: build-vita/Dusk.vpk
|
||||
# if-no-files-found: error
|
||||
|
||||
build-knulli:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
+1
-3
@@ -4,13 +4,11 @@
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Setup
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
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)
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
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;
|
||||
@@ -1,21 +0,0 @@
|
||||
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,14 @@
|
||||
module('entity')
|
||||
module('entityposition')
|
||||
module('entitymaterial')
|
||||
module('glm')
|
||||
module('color')
|
||||
|
||||
-- Position
|
||||
local posComp = entityAddComponent(ENTITY_ID, COMPONENT_TYPE_POSITION)
|
||||
entityPositionSetPosition(ENTITY_ID, posComp, vec3(1, 2, 3))
|
||||
|
||||
-- Material
|
||||
local matComp = entityAddComponent(ENTITY_ID, COMPONENT_TYPE_MATERIAL)
|
||||
local material = entityMaterialGetShaderMaterial(ENTITY_ID, matComp)
|
||||
material.unlit.color = colorBlue()
|
||||
@@ -1,77 +0,0 @@
|
||||
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');
|
||||
@@ -0,0 +1,76 @@
|
||||
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("circle", INPUT_ACTION_CANCEL)
|
||||
inputBind("cross", INPUT_ACTION_ACCEPT)
|
||||
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
|
||||
@@ -1,32 +0,0 @@
|
||||
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;
|
||||
@@ -1,96 +0,0 @@
|
||||
# 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,7 +4,6 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_INPUT_GAMEPAD
|
||||
DUSK_DISPLAY_WIDTH=640
|
||||
DUSK_DISPLAY_HEIGHT=480
|
||||
DUSK_THREAD_PTHREAD
|
||||
)
|
||||
|
||||
# Custom compiler flags
|
||||
@@ -22,9 +21,31 @@ set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
|
||||
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
|
||||
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
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
cglm
|
||||
liblua
|
||||
m
|
||||
fat
|
||||
PkgConfig::zip
|
||||
|
||||
@@ -2,9 +2,4 @@ include(cmake/targets/dolphin.cmake)
|
||||
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_GAMECUBE
|
||||
)
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
# bba
|
||||
)
|
||||
)
|
||||
@@ -1,7 +1,6 @@
|
||||
# Find link platform-specific libraries
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
# find_package(CURL REQUIRED)
|
||||
|
||||
# Setup endianess at compile time to optimize.
|
||||
include(TestBigEndian)
|
||||
@@ -17,20 +16,18 @@ else()
|
||||
endif()
|
||||
|
||||
# Link required libraries.
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
SDL2
|
||||
pthread
|
||||
OpenGL::GL
|
||||
GL
|
||||
m
|
||||
# CURL::libcurl
|
||||
)
|
||||
|
||||
# Define platform-specific macros.
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_SDL2
|
||||
DUSK_OPENGL
|
||||
DUSK_CONSOLE_POSIX
|
||||
# DUSK_OPENGL_LEGACY
|
||||
DUSK_LINUX
|
||||
DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
@@ -41,6 +38,4 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_INPUT_POINTER
|
||||
DUSK_INPUT_GAMEPAD
|
||||
DUSK_TIME_DYNAMIC
|
||||
DUSK_NETWORK_IPV6
|
||||
DUSK_THREAD_PTHREAD
|
||||
)
|
||||
+3
-26
@@ -1,19 +1,6 @@
|
||||
# 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(OpenGL REQUIRED)
|
||||
target_link_libraries(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
${SDL2_LIBRARIES}
|
||||
SDL2
|
||||
pthread
|
||||
@@ -37,22 +24,13 @@ target_link_libraries(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
pspvfpu
|
||||
pspvram
|
||||
psphprm
|
||||
pspnet
|
||||
pspnet_inet
|
||||
pspnet_apctl
|
||||
psphttp
|
||||
pspssl
|
||||
|
||||
jerryscript::core
|
||||
jerryscript::ext
|
||||
jerryscript::port
|
||||
)
|
||||
|
||||
target_include_directories(${DUSK_BINARY_TARGET_NAME} PRIVATE
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_SDL2
|
||||
DUSK_OPENGL
|
||||
DUSK_PSP
|
||||
@@ -61,7 +39,6 @@ target_compile_definitions(${DUSK_BINARY_TARGET_NAME} PUBLIC
|
||||
DUSK_OPENGL_LEGACY
|
||||
DUSK_DISPLAY_WIDTH=480
|
||||
DUSK_DISPLAY_HEIGHT=272
|
||||
DUSK_THREAD_PTHREAD
|
||||
)
|
||||
|
||||
# Postbuild, create .pbp file for PSP.
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
if(NOT DEFINED ENV{VITASDK})
|
||||
message(FATAL_ERROR "VITASDK environment variable is not set.")
|
||||
endif()
|
||||
|
||||
include("$ENV{VITASDK}/share/vita.cmake" REQUIRED)
|
||||
|
||||
set(VITA_APP_NAME "Dusk")
|
||||
set(VITA_TITLEID "DUSK00001")
|
||||
set(VITA_VERSION "01.00")
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
|
||||
# Custom flags for cglm
|
||||
set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
|
||||
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
|
||||
find_package(cglm REQUIRED)
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
${SDL2_LIBRARIES}
|
||||
cglm
|
||||
SDL2
|
||||
SDL2main
|
||||
zip
|
||||
bz2
|
||||
z
|
||||
zstd
|
||||
crypto
|
||||
lzma
|
||||
m
|
||||
pthread
|
||||
stdc++
|
||||
vitaGL
|
||||
mathneon
|
||||
vitashark
|
||||
kubridge_stub
|
||||
SceAppMgr_stub
|
||||
SceAudio_stub
|
||||
SceCtrl_stub
|
||||
SceCommonDialog_stub
|
||||
SceDisplay_stub
|
||||
SceKernelDmacMgr_stub
|
||||
SceGxm_stub
|
||||
SceShaccCg_stub
|
||||
SceSysmodule_stub
|
||||
ScePower_stub
|
||||
SceTouch_stub
|
||||
SceVshBridge_stub
|
||||
SceIofilemgr_stub
|
||||
SceShaccCgExt
|
||||
libtaihen_stub.a
|
||||
|
||||
|
||||
# SceKernel_stub
|
||||
SceAppUtil_stub
|
||||
SceHid_stub
|
||||
SceRtc_stub
|
||||
)
|
||||
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
${SDL2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
DUSK_SDL2
|
||||
DUSK_OPENGL
|
||||
DUSK_VITA
|
||||
DUSK_INPUT_GAMEPAD
|
||||
DUSK_PLATFORM_ENDIAN_LITTLE
|
||||
DUSK_OPENGL_LEGACY
|
||||
DUSK_DISPLAY_WIDTH=960
|
||||
DUSK_DISPLAY_HEIGHT=544
|
||||
)
|
||||
|
||||
# Post-build: create SELF from the ELF binary (UNSAFE = homebrew, no signing)
|
||||
vita_create_self(${DUSK_BINARY_TARGET_NAME}.self ${DUSK_BINARY_TARGET_NAME} UNSAFE)
|
||||
|
||||
# Post-build: package SELF + assets into a .vpk installable on the Vita
|
||||
vita_create_vpk(${DUSK_BINARY_TARGET_NAME}.vpk ${VITA_TITLEID} ${DUSK_BINARY_TARGET_NAME}.self
|
||||
VERSION ${VITA_VERSION}
|
||||
NAME ${VITA_APP_NAME}
|
||||
FILE ${DUSK_ASSETS_ZIP} dusk.dsk
|
||||
)
|
||||
@@ -1,13 +0,0 @@
|
||||
FROM vitasdk/vitasdk:latest
|
||||
WORKDIR /workdir
|
||||
|
||||
# Install vitaGL and its dependencies (vitashark, SceShaccCg) via vdpm
|
||||
RUN which vdpm
|
||||
|
||||
# Install Python (needed for Dusk code generation tools)
|
||||
RUN apk add --no-cache \
|
||||
python3 \
|
||||
py3-pip \
|
||||
py3-dotenv
|
||||
|
||||
VOLUME ["/workdir"]
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
docker build -t dusk-vita -f docker/vita/Dockerfile .
|
||||
docker run --rm -v $(pwd):/workdir dusk-vita /bin/bash -c "./scripts/build-vita.sh"
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$VITASDK" ]; then
|
||||
echo "VITASDK environment variable is not set. Please set it to the path of your VitaSDK installation."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-vita
|
||||
cd build-vita
|
||||
cmake \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$VITASDK/share/vita.toolchain.cmake \
|
||||
-DDUSK_TARGET_SYSTEM=vita \
|
||||
..
|
||||
make -j$(nproc)
|
||||
+1
-6
@@ -16,12 +16,7 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
|
||||
add_subdirectory(dusksdl2)
|
||||
add_subdirectory(duskgl)
|
||||
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "vita")
|
||||
add_subdirectory(duskvita)
|
||||
add_subdirectory(dusksdl2)
|
||||
add_subdirectory(duskgl)
|
||||
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "wii" OR DUSK_TARGET_SYSTEM STREQUAL "gamecube")
|
||||
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
|
||||
add_subdirectory(duskdolphin)
|
||||
|
||||
endif()
|
||||
+17
-12
@@ -32,13 +32,18 @@ if(NOT yyjson_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT jerryscript_FOUND)
|
||||
find_package(jerryscript REQUIRED)
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
|
||||
jerryscript::core
|
||||
jerryscript::ext
|
||||
jerryscript::port
|
||||
)
|
||||
if(NOT Lua_FOUND)
|
||||
find_package(Lua REQUIRED)
|
||||
if(Lua_FOUND AND NOT TARGET Lua::Lua)
|
||||
add_library(Lua::Lua INTERFACE IMPORTED)
|
||||
set_target_properties(
|
||||
Lua::Lua
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
|
||||
)
|
||||
endif()
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC Lua::Lua)
|
||||
endif()
|
||||
|
||||
# Includes
|
||||
@@ -56,9 +61,8 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
|
||||
# Subdirs
|
||||
add_subdirectory(assert)
|
||||
add_subdirectory(asset)
|
||||
add_subdirectory(console)
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(log)
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(entity)
|
||||
add_subdirectory(error)
|
||||
@@ -68,9 +72,10 @@ add_subdirectory(locale)
|
||||
add_subdirectory(physics)
|
||||
add_subdirectory(scene)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(time)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(network)
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(thread)
|
||||
|
||||
# if(DUSK_TARGET_SYSTEM STREQUAL "linux" OR DUSK_TARGET_SYSTEM STREQUAL "psp")
|
||||
# add_subdirectory(thread)
|
||||
# endif()
|
||||
@@ -25,7 +25,7 @@ errorret_t assetInit(void) {
|
||||
}
|
||||
|
||||
bool_t assetFileExists(const char_t *filename) {
|
||||
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long.");
|
||||
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
|
||||
|
||||
zip_int64_t idx = zip_name_locate(ASSET.zip, filename, 0);
|
||||
if(idx < 0) return false;
|
||||
@@ -38,7 +38,7 @@ errorret_t assetLoad(
|
||||
void *params,
|
||||
void *output
|
||||
) {
|
||||
assertStrLenMax(filename, ASSET_FILE_PATH_MAX, "Filename too long.");
|
||||
assertStrLenMax(filename, FILENAME_MAX, "Filename too long.");
|
||||
assertNotNull(output, "Output pointer cannot be NULL.");
|
||||
assertNotNull(loader, "Asset file loader cannot be NULL.");
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#include "error/error.h"
|
||||
#include <zip.h>
|
||||
|
||||
#define ASSET_FILE_PATH_MAX FILENAME_MAX
|
||||
|
||||
typedef struct assetfile_s assetfile_t;
|
||||
|
||||
typedef errorret_t (*assetfileloader_t)(assetfile_t *file);
|
||||
|
||||
@@ -1,92 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "assetscriptloader.h"
|
||||
#include "assert/assert.h"
|
||||
#include <stdlib.h>
|
||||
#include <zip.h>
|
||||
|
||||
errorret_t assetScriptLoader(assetfile_t *file) {
|
||||
assertNotNull(file, "Asset file cannot be NULL");
|
||||
assertNull(file->zipFile, "Asset file zip handle must be NULL before open");
|
||||
assertNull(file->zipFile, "Asset file zip handle must be NULL");
|
||||
assertNotNull(file->output, "Asset file output cannot be NULL");
|
||||
|
||||
assetscript_t *script = (assetscript_t *)file->output;
|
||||
|
||||
// Open the asset for buffering
|
||||
errorChain(assetFileOpen(file));
|
||||
|
||||
// Accumulate full source into a dynamically grown buffer.
|
||||
size_t srcLen = 0;
|
||||
size_t capacity = ASSET_SCRIPT_CHUNK_SIZE;
|
||||
char_t *src = (char_t *)malloc(capacity + 1);
|
||||
if(!src) {
|
||||
assetFileClose(file);
|
||||
errorThrow("Out of memory reading script: %s", file->filename);
|
||||
// Request loading
|
||||
if(!lua_load(
|
||||
script->ctx->luaState,
|
||||
assetScriptReader,
|
||||
file,
|
||||
file->filename,
|
||||
NULL
|
||||
) == 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);
|
||||
}
|
||||
|
||||
while(1) {
|
||||
if(srcLen + ASSET_SCRIPT_CHUNK_SIZE > capacity) {
|
||||
capacity = srcLen + ASSET_SCRIPT_CHUNK_SIZE;
|
||||
char_t *tmp = (char_t *)realloc(src, capacity + 1);
|
||||
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);
|
||||
// Now loaded, exec
|
||||
if(lua_pcall(script->ctx->luaState, 0, LUA_MULTRET, 0) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(script->ctx->luaState, -1);
|
||||
lua_pop(script->ctx->luaState, 1);
|
||||
errorThrow("Failed to execute Lua script: %s", strErr);
|
||||
}
|
||||
|
||||
if(script->resultOut != NULL) {
|
||||
*(script->resultOut) = result;
|
||||
} else {
|
||||
jerry_value_free(result);
|
||||
}
|
||||
|
||||
return closeRet;
|
||||
// Close the file
|
||||
return assetFileClose(file);
|
||||
}
|
||||
|
||||
errorret_t assetScriptLoad(
|
||||
const char_t *path,
|
||||
jerry_value_t *resultOut
|
||||
) {
|
||||
errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx) {
|
||||
assertNotNull(path, "Script path cannot be NULL");
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
assetscript_t script;
|
||||
script.ctx = ctx;
|
||||
|
||||
assetscript_t scriptData;
|
||||
scriptData.resultOut = resultOut;
|
||||
|
||||
return assetLoad(path, assetScriptLoader, NULL, &scriptData);
|
||||
return assetLoad(
|
||||
path,
|
||||
assetScriptLoader,
|
||||
NULL,
|
||||
&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,23 +1,28 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "asset/asset.h"
|
||||
#include "script/scriptcontext.h"
|
||||
|
||||
#define ASSET_SCRIPT_CHUNK_SIZE 1024
|
||||
#define ASSET_SCRIPT_BUFFER_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
jerry_value_t *resultOut;
|
||||
void *nothing;
|
||||
} assetscriptloaderparams_t;
|
||||
|
||||
typedef struct {
|
||||
scriptcontext_t *ctx;
|
||||
char_t buffer[ASSET_SCRIPT_BUFFER_SIZE];
|
||||
} assetscript_t;
|
||||
|
||||
/**
|
||||
* Handler for script assets. Reads the full source, evaluates it with
|
||||
* JerryScript, and optionally stores the result.
|
||||
*
|
||||
* Handler for script assets.
|
||||
*
|
||||
* @param file Asset file to load the script from.
|
||||
* @return Any error that occurs during loading.
|
||||
*/
|
||||
@@ -25,14 +30,19 @@ errorret_t assetScriptLoader(assetfile_t *file);
|
||||
|
||||
/**
|
||||
* Loads a script from the specified path.
|
||||
*
|
||||
* @param path Path to the script asset.
|
||||
* @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.
|
||||
*
|
||||
* @param path Path to the script asset.
|
||||
* @param ctx Script context to load the script into.
|
||||
* @return Any error that occurs during loading.
|
||||
*/
|
||||
errorret_t assetScriptLoad(
|
||||
const char_t *path,
|
||||
jerry_value_t *resultOut
|
||||
);
|
||||
errorret_t assetScriptLoad(const char_t *path, scriptcontext_t *ctx);
|
||||
|
||||
/**
|
||||
* 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,9 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
console.c
|
||||
)
|
||||
@@ -1,193 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* 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,6 +25,8 @@
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "time/time.h"
|
||||
|
||||
#include "script/module/display/moduleshader.h"
|
||||
|
||||
display_t DISPLAY = { 0 };
|
||||
|
||||
errorret_t displayInit(void) {
|
||||
@@ -62,7 +64,7 @@ errorret_t displayInit(void) {
|
||||
|
||||
glm_perspective(
|
||||
glm_rad(45.0f),
|
||||
SCREEN.aspect,
|
||||
(float_t)SCREEN.width / (float_t)SCREEN.height,
|
||||
0.1f,
|
||||
100.0f,
|
||||
proj
|
||||
@@ -99,6 +101,9 @@ errorret_t displayUpdate(void) {
|
||||
|
||||
errorChain(sceneRender());
|
||||
|
||||
// Render UI
|
||||
// uiRender();
|
||||
|
||||
// Finish up
|
||||
screenUnbind();
|
||||
screenRender();
|
||||
|
||||
@@ -40,17 +40,6 @@ uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer) {
|
||||
return frameBufferPlatformGetHeight(framebuffer);
|
||||
}
|
||||
|
||||
float_t frameBufferGetAspect(const framebuffer_t *framebuffer) {
|
||||
#ifdef frameBufferPlatformGetAspect
|
||||
return frameBufferPlatformGetAspect(framebuffer);
|
||||
#endif
|
||||
|
||||
uint32_t width = frameBufferGetWidth(framebuffer);
|
||||
uint32_t height = frameBufferGetHeight(framebuffer);
|
||||
if(height == 0) return 1.0f; // Avoid divide by zero, just return 1:1 aspect.
|
||||
return (float_t)width / (float_t)height;
|
||||
}
|
||||
|
||||
void frameBufferClear(const uint8_t flags, const color_t color) {
|
||||
frameBufferPlatformClear(flags, color);
|
||||
}
|
||||
|
||||
@@ -58,16 +58,6 @@ uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer);
|
||||
*/
|
||||
uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer);
|
||||
|
||||
/**
|
||||
* Returns the aspect ratio of the framebuffer. This is ALMOST always just
|
||||
* the width / height, however some platforms may choose to override this if
|
||||
* they have stretched styled back buffers, e.g. 640x480 stretched.
|
||||
*
|
||||
* @param framebuffer The framebuffer to get the aspect ratio of.
|
||||
* @return The aspect ratio of the framebuffer.
|
||||
*/
|
||||
float_t frameBufferGetAspect(const framebuffer_t *framebuffer);
|
||||
|
||||
/**
|
||||
* Binds the framebuffer for rendering, or the backbuffer if the framebuffer
|
||||
* provided is NULL.
|
||||
|
||||
@@ -52,7 +52,7 @@ errorret_t screenBind() {
|
||||
// Screen mode backbuffer uses the full display size
|
||||
SCREEN.width = frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
SCREEN.height = frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
SCREEN.aspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
|
||||
// No needd for a framebuffer.
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
@@ -100,7 +100,8 @@ errorret_t screenBind() {
|
||||
int32_t fbWidth, fbHeight;
|
||||
fbWidth = frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
fbHeight = frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
float_t currentAspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
|
||||
|
||||
float_t currentAspect = (float_t)fbWidth / (float_t)fbHeight;
|
||||
if(currentAspect == SCREEN.aspectRatio.ratio) {
|
||||
// No need to use framebuffer.
|
||||
SCREEN.width = fbWidth;
|
||||
@@ -128,14 +129,13 @@ errorret_t screenBind() {
|
||||
if(SCREEN.framebufferReady) {
|
||||
// Is current framebuffer the correct size?
|
||||
int32_t curFbWidth, curFbHeight;
|
||||
float_t curFbAspect = frameBufferGetAspect(&SCREEN.framebuffer);
|
||||
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
|
||||
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
|
||||
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
|
||||
// Correct size, nothing to do.
|
||||
SCREEN.width = newFbWidth;
|
||||
SCREEN.height = newFbHeight;
|
||||
SCREEN.aspect = curFbAspect;
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -13,19 +13,19 @@
|
||||
#include "asset/loader/display/assettilesetloader.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
texture_t FONT_TEXTURE_DEFAULT;
|
||||
tileset_t FONT_TILESET_DEFAULT;
|
||||
texture_t DEFAULT_FONT_TEXTURE;
|
||||
tileset_t DEFAULT_FONT_TILESET;
|
||||
|
||||
errorret_t textInit(void) {
|
||||
errorChain(assetTextureLoad(
|
||||
"ui/minogram.png", &FONT_TEXTURE_DEFAULT, TEXTURE_FORMAT_RGBA
|
||||
"ui/minogram.png", &DEFAULT_FONT_TEXTURE, TEXTURE_FORMAT_RGBA
|
||||
));
|
||||
errorChain(assetTilesetLoad("ui/minogram.dtf", &FONT_TILESET_DEFAULT));
|
||||
errorChain(assetTilesetLoad("ui/minogram.dtf", &DEFAULT_FONT_TILESET));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t textDispose(void) {
|
||||
errorChain(textureDispose(&FONT_TEXTURE_DEFAULT));
|
||||
errorChain(textureDispose(&DEFAULT_FONT_TEXTURE));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -70,7 +70,9 @@ errorret_t textDraw(
|
||||
const float_t x,
|
||||
const float_t y,
|
||||
const char_t *text,
|
||||
const color_t color,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
const tileset_t *tileset,
|
||||
texture_t *texture
|
||||
) {
|
||||
@@ -81,11 +83,6 @@ errorret_t textDraw(
|
||||
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, texture));
|
||||
|
||||
#if MESH_ENABLE_COLOR
|
||||
#else
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, color));
|
||||
#endif
|
||||
|
||||
// errorChain(spriteBatchPush(
|
||||
// // texture,
|
||||
// posX, posY,
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#define TEXT_CHAR_START '!'
|
||||
|
||||
extern texture_t FONT_TEXTURE_DEFAULT;
|
||||
extern tileset_t FONT_TILESET_DEFAULT;
|
||||
extern texture_t DEFAULT_FONT_TEXTURE;
|
||||
extern tileset_t DEFAULT_FONT_TILESET;
|
||||
|
||||
/**
|
||||
* Initializes the text system.
|
||||
@@ -66,7 +66,9 @@ errorret_t textDraw(
|
||||
const float_t x,
|
||||
const float_t y,
|
||||
const char_t *text,
|
||||
const color_t color,
|
||||
#if MESH_ENABLE_COLOR
|
||||
const color_t color,
|
||||
#endif
|
||||
const tileset_t *tileset,
|
||||
texture_t *texture
|
||||
);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <cglm/cglm.h>
|
||||
#include <cglm/types.h>
|
||||
#include <cglm/vec2.h>
|
||||
#include <jerryscript.h>
|
||||
|
||||
#include "duskplatform.h"
|
||||
|
||||
|
||||
+57
-28
@@ -20,22 +20,15 @@
|
||||
#include "entity/component/physics/entityphysics.h"
|
||||
#include "game/game.h"
|
||||
#include "physics/physicsmanager.h"
|
||||
#include "network/network.h"
|
||||
#include "system/system.h"
|
||||
#include "console/console.h"
|
||||
|
||||
double jerry_port_current_time(void) {
|
||||
dusktimeepoch_t epoch = timeGetEpoch();
|
||||
return epoch.time * 1000.0;
|
||||
}
|
||||
|
||||
int32_t jerry_port_local_tza(double unix_ms) {
|
||||
(void) unix_ms;
|
||||
return 0;
|
||||
}
|
||||
#include "display/mesh/cube.h"
|
||||
#include "display/mesh/plane.h"
|
||||
|
||||
engine_t ENGINE;
|
||||
|
||||
/* Kept module-level only because engineUpdate needs them for the reset. */
|
||||
static entityid_t phBoxEnt;
|
||||
static componentid_t phBoxPhys;
|
||||
|
||||
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
memoryZero(&ENGINE, sizeof(engine_t));
|
||||
ENGINE.running = true;
|
||||
@@ -43,9 +36,7 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
ENGINE.argv = argv;
|
||||
|
||||
// Init systems. Order is important.
|
||||
errorChain(systemInit());
|
||||
timeInit();
|
||||
consoleInit();
|
||||
errorChain(inputInit());
|
||||
errorChain(assetInit());
|
||||
errorChain(localeManagerInit());
|
||||
@@ -55,33 +46,74 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
errorChain(sceneInit());
|
||||
entityManagerInit();
|
||||
physicsManagerInit();
|
||||
errorChain(networkInit());
|
||||
errorChain(gameInit());
|
||||
|
||||
/* ---- 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, distance * 6.0f);
|
||||
|
||||
/* ---- Static floor (visual + physics) ---- */
|
||||
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;
|
||||
|
||||
// Test Box
|
||||
phBoxEnt = entityManagerAdd();
|
||||
componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH);
|
||||
// componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL);
|
||||
entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE);
|
||||
// entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED;
|
||||
componentid_t boxScript = entityAddComponent(phBoxEnt, COMPONENT_TYPE_SCRIPT);
|
||||
errorChain(entityScriptExecAsset(phBoxEnt, boxScript, "entity/test.lua"));
|
||||
|
||||
/* Run the init script. */
|
||||
consolePrint("Engine initialized");
|
||||
errorChain(scriptManagerExecFile("init.js", NULL));
|
||||
scriptcontext_t ctx;
|
||||
errorChain(scriptContextInit(&ctx));
|
||||
errorChain(scriptContextExecFile(&ctx, "init.lua"));
|
||||
scriptContextDispose(&ctx);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t engineUpdate(void) {
|
||||
// Order here is important.
|
||||
errorChain(networkUpdate());
|
||||
timeUpdate();
|
||||
inputUpdate();
|
||||
consoleUpdate();
|
||||
|
||||
uiUpdate();
|
||||
errorChain(sceneUpdate());
|
||||
|
||||
/* Step physics: positions are updated directly on POSITION components. */
|
||||
physicsManagerUpdate();
|
||||
|
||||
errorChain(gameUpdate());
|
||||
errorChain(displayUpdate());
|
||||
|
||||
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
||||
|
||||
// Scene update occurs last because only after rendering would we want to do
|
||||
// scene switching, refer to sceneSet() for information.
|
||||
errorChain(sceneUpdate());
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -92,13 +124,10 @@ void engineExit(void) {
|
||||
errorret_t engineDispose(void) {
|
||||
sceneDispose();
|
||||
errorChain(gameDispose());
|
||||
errorChain(networkDispose());
|
||||
entityManagerDispose();
|
||||
localeManagerDispose();
|
||||
uiDispose();
|
||||
consoleDispose();
|
||||
errorChain(displayDispose());
|
||||
errorChain(assetDispose());
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -12,14 +12,8 @@
|
||||
componentdefinition_t COMPONENT_DEFINITIONS[] = {
|
||||
[COMPONENT_TYPE_NULL] = { 0 },
|
||||
|
||||
#define X(enm, type, field, iMethod, dMethod) \
|
||||
[COMPONENT_TYPE_##enm] = { \
|
||||
.enumName = #enm, \
|
||||
.name = #field, \
|
||||
.init = iMethod, \
|
||||
.dispose = dMethod \
|
||||
},
|
||||
|
||||
#define X(enumName, type, field, iMethod, dMethod) \
|
||||
[COMPONENT_TYPE_##enumName] = { .init = iMethod, .dispose = dMethod },
|
||||
#include "componentlist.h"
|
||||
#undef X
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ typedef union {
|
||||
} componentdata_t;
|
||||
|
||||
typedef struct {
|
||||
const char_t *enumName;
|
||||
const char_t *name;
|
||||
void (*init)(const entityid_t, const componentid_t);
|
||||
void (*dispose)(const entityid_t, const componentid_t);
|
||||
} componentdefinition_t;
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
*/
|
||||
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/entity.h"
|
||||
#include "entity/component/display/entityposition.h"
|
||||
#include "display/framebuffer/framebuffer.h"
|
||||
#include "display/screen/screen.h"
|
||||
|
||||
@@ -21,6 +19,101 @@ void entityCameraInit(const entityid_t ent, const componentid_t comp) {
|
||||
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;
|
||||
}
|
||||
|
||||
entitycameraprojectiontype_t entityCameraGetProjType(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
return cam->projType;
|
||||
}
|
||||
|
||||
void entityCameraSetProjType(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const entitycameraprojectiontype_t type
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
cam->projType = type;
|
||||
}
|
||||
|
||||
float_t entityCameraGetFov(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
return cam->perspective.fov;
|
||||
}
|
||||
|
||||
void entityCameraSetFov(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t fov
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
cam->perspective.fov = fov;
|
||||
}
|
||||
|
||||
void entityCameraSetOrthographic(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t left,
|
||||
const float_t right,
|
||||
const float_t top,
|
||||
const float_t bottom
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
cam->orthographic.left = left;
|
||||
cam->orthographic.right = right;
|
||||
cam->orthographic.top = top;
|
||||
cam->orthographic.bottom = bottom;
|
||||
}
|
||||
|
||||
void entityCameraGetProjection(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
@@ -58,38 +151,4 @@ void entityCameraGetProjection(
|
||||
out
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
entityid_t entityCameraGetCurrent(void) {
|
||||
entityid_t camEnts[ENTITY_COUNT_MAX];
|
||||
componentid_t camComps[ENTITY_COUNT_MAX];
|
||||
entityid_t count = componentGetEntitiesWithComponent(
|
||||
COMPONENT_TYPE_CAMERA, camEnts, camComps
|
||||
);
|
||||
if(count == 0) return ENTITY_COUNT_MAX;
|
||||
return camEnts[0];
|
||||
}
|
||||
|
||||
void entityCameraGetForward(const entityid_t entityId, vec2 out) {
|
||||
componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION);
|
||||
entityposition_t *pos = entityPositionGet(entityId, posComp);
|
||||
// View matrix column layout: M[col][row], forward = {-M[0][2], -M[1][2], -M[2][2]}
|
||||
float_t fx = -pos->transform[0][2];
|
||||
float_t fz = -pos->transform[2][2];
|
||||
float_t len = sqrtf(fx * fx + fz * fz);
|
||||
if(len > 1e-6f) { fx /= len; fz /= len; }
|
||||
out[0] = fx;
|
||||
out[1] = fz;
|
||||
}
|
||||
|
||||
void entityCameraGetRight(const entityid_t entityId, vec2 out) {
|
||||
componentid_t posComp = entityGetComponent(entityId, COMPONENT_TYPE_POSITION);
|
||||
entityposition_t *pos = entityPositionGet(entityId, posComp);
|
||||
// View matrix column layout: right = {M[0][0], M[1][0], M[2][0]}
|
||||
float_t rx = pos->transform[0][0];
|
||||
float_t rz = pos->transform[2][0];
|
||||
float_t len = sqrtf(rx * rx + rz * rz);
|
||||
if(len > 1e-6f) { rx /= len; rz /= len; }
|
||||
out[0] = rx;
|
||||
out[1] = rz;
|
||||
}
|
||||
@@ -55,24 +55,114 @@ void entityCameraGetProjection(
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the entity ID of the first active camera, or ENTITY_COUNT_MAX if none.
|
||||
* Gets the near clip distance of a camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @return The near clip distance.
|
||||
*/
|
||||
entityid_t entityCameraGetCurrent(void);
|
||||
float_t entityCameraGetZNear(const entityid_t ent, const componentid_t comp);
|
||||
|
||||
/**
|
||||
* Gets the camera's horizontal forward direction (XZ plane) from its position
|
||||
* component. Automatically finds the position component on the entity.
|
||||
* Sets the near clip distance of a camera.
|
||||
*
|
||||
* @param entityId The camera entity ID.
|
||||
* @param out Output vec2: {forwardX, forwardZ} normalized.
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @param zNear The near clip distance.
|
||||
*/
|
||||
void entityCameraGetForward(const entityid_t entityId, vec2 out);
|
||||
void entityCameraSetZNear(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t zNear
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the camera's horizontal right direction (XZ plane) from its position
|
||||
* component. Automatically finds the position component on the entity.
|
||||
* Gets the far clip distance of a camera.
|
||||
*
|
||||
* @param entityId The camera entity ID.
|
||||
* @param out Output vec2: {rightX, rightZ} normalized.
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @return The far clip distance.
|
||||
*/
|
||||
void entityCameraGetRight(const entityid_t entityId, vec2 out);
|
||||
float_t entityCameraGetZFar(const entityid_t ent, const componentid_t comp);
|
||||
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the projection type of a camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @return The projection type.
|
||||
*/
|
||||
entitycameraprojectiontype_t entityCameraGetProjType(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the projection type of a camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @param type The projection type.
|
||||
*/
|
||||
void entityCameraSetProjType(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const entitycameraprojectiontype_t type
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the field of view (in radians) of a perspective camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @return The field of view in radians.
|
||||
*/
|
||||
float_t entityCameraGetFov(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the field of view (in radians) of a perspective camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @param fov The field of view in radians.
|
||||
*/
|
||||
void entityCameraSetFov(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t fov
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the orthographic projection bounds of a camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @param left Left bound.
|
||||
* @param right Right bound.
|
||||
* @param top Top bound.
|
||||
* @param bottom Bottom bound.
|
||||
*/
|
||||
void entityCameraSetOrthographic(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t left,
|
||||
const float_t right,
|
||||
const float_t top,
|
||||
const float_t bottom
|
||||
);
|
||||
@@ -48,15 +48,4 @@ void entityMaterialSetShader(
|
||||
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||
);
|
||||
mat->shader = shader;
|
||||
}
|
||||
|
||||
void entityMaterialSetColor(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const color_t color
|
||||
) {
|
||||
entitymaterial_t *mat = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||
);
|
||||
mat->material.unlit.color = color;
|
||||
}
|
||||
@@ -60,17 +60,4 @@ void entityMaterialSetShader(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
shader_t *shader
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the unlit color for the given entity and component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param color The color to set.
|
||||
*/
|
||||
void entityMaterialSetColor(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const color_t color
|
||||
);
|
||||
@@ -5,13 +5,7 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entitymesh.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "display/mesh/cube.h"
|
||||
#include "display/mesh/plane.h"
|
||||
#include "display/mesh/capsule.h"
|
||||
|
||||
void entityMeshInit(
|
||||
const entityid_t entityId,
|
||||
@@ -20,9 +14,7 @@ void entityMeshInit(
|
||||
entitymesh_t *comp = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
comp->mesh = &CUBE_MESH_SIMPLE;
|
||||
comp->ownedVertices = NULL;
|
||||
comp->ownsData = false;
|
||||
comp->mesh = NULL;
|
||||
}
|
||||
|
||||
mesh_t * entityMeshGetMesh(
|
||||
@@ -44,100 +36,4 @@ void entityMeshSetMesh(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
comp->mesh = mesh;
|
||||
}
|
||||
|
||||
void entityMeshDispose(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entitymesh_t *comp = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
if(!comp->ownsData) return;
|
||||
(void)meshDispose(&comp->ownedMesh);
|
||||
memoryFree(comp->ownedVertices);
|
||||
comp->ownedVertices = NULL;
|
||||
comp->ownsData = false;
|
||||
comp->mesh = NULL;
|
||||
}
|
||||
|
||||
errorret_t entityMeshGeneratePlane(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const float_t width,
|
||||
const float_t height
|
||||
) {
|
||||
entitymesh_t *comp = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
entityMeshDispose(entityId, componentId);
|
||||
|
||||
comp->ownedVertices = memoryAllocate(PLANE_VERTEX_COUNT * sizeof(meshvertex_t));
|
||||
assertNotNull(comp->ownedVertices, "Failed to allocate plane vertices");
|
||||
|
||||
vec3 min = { -width * 0.5f, 0.0f, -height * 0.5f };
|
||||
vec3 max = { width * 0.5f, 0.0f, height * 0.5f };
|
||||
vec2 uvMin = { 0.0f, 0.0f };
|
||||
vec2 uvMax = { 1.0f, 1.0f };
|
||||
planeBuffer(
|
||||
comp->ownedVertices,
|
||||
PLANE_AXIS_XZ,
|
||||
min,
|
||||
max
|
||||
#if MESH_ENABLE_COLOR
|
||||
, COLOR_WHITE_4B
|
||||
#endif
|
||||
, uvMin,
|
||||
uvMax
|
||||
);
|
||||
|
||||
errorChain(meshInit(
|
||||
&comp->ownedMesh,
|
||||
PLANE_PRIMITIVE_TYPE,
|
||||
PLANE_VERTEX_COUNT,
|
||||
comp->ownedVertices
|
||||
));
|
||||
|
||||
comp->mesh = &comp->ownedMesh;
|
||||
comp->ownsData = true;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t entityMeshGenerateCapsule(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const float_t radius,
|
||||
const float_t halfHeight
|
||||
) {
|
||||
entitymesh_t *comp = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
entityMeshDispose(entityId, componentId);
|
||||
|
||||
comp->ownedVertices = memoryAllocate(CAPSULE_VERTEX_COUNT * sizeof(meshvertex_t));
|
||||
assertNotNull(comp->ownedVertices, "Failed to allocate capsule vertices");
|
||||
|
||||
vec3 center = { 0.0f, 0.0f, 0.0f };
|
||||
capsuleBuffer(
|
||||
comp->ownedVertices,
|
||||
center,
|
||||
radius,
|
||||
halfHeight,
|
||||
CAPSULE_CAP_RINGS,
|
||||
CAPSULE_SECTORS
|
||||
#if MESH_ENABLE_COLOR
|
||||
, COLOR_WHITE_4B
|
||||
#endif
|
||||
);
|
||||
|
||||
errorChain(meshInit(
|
||||
&comp->ownedMesh,
|
||||
CAPSULE_PRIMITIVE_TYPE,
|
||||
CAPSULE_VERTEX_COUNT,
|
||||
comp->ownedVertices
|
||||
));
|
||||
|
||||
comp->mesh = &comp->ownedMesh;
|
||||
comp->ownsData = true;
|
||||
errorOk();
|
||||
}
|
||||
@@ -11,9 +11,6 @@
|
||||
|
||||
typedef struct {
|
||||
mesh_t *mesh;
|
||||
mesh_t ownedMesh;
|
||||
meshvertex_t *ownedVertices;
|
||||
bool_t ownsData;
|
||||
} entitymesh_t;
|
||||
|
||||
/**
|
||||
@@ -50,47 +47,4 @@ void entityMeshSetMesh(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
mesh_t *mesh
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes the entity mesh component, freeing any owned mesh data.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
*/
|
||||
void entityMeshDispose(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Generates an XZ-aligned plane mesh owned by the component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param width Width of the plane along the X axis.
|
||||
* @param height Height of the plane along the Z axis.
|
||||
* @return Error return value.
|
||||
*/
|
||||
errorret_t entityMeshGeneratePlane(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const float_t width,
|
||||
const float_t height
|
||||
);
|
||||
|
||||
/**
|
||||
* Generates a Y-axis capsule mesh owned by the component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param radius Radius of the cylinder and hemisphere caps.
|
||||
* @param halfHeight Half-height of the cylindrical section.
|
||||
* @return Error return value.
|
||||
*/
|
||||
errorret_t entityMeshGenerateCapsule(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const float_t radius,
|
||||
const float_t halfHeight
|
||||
);
|
||||
@@ -100,6 +100,15 @@ bool_t entityPhysicsIsOnGround(
|
||||
return phys->onGround;
|
||||
}
|
||||
|
||||
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 entityPhysicsSetBodyType(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
@@ -110,13 +119,23 @@ void entityPhysicsSetBodyType(
|
||||
phys->type = type;
|
||||
}
|
||||
|
||||
physicsbodytype_t entityPhysicsGetBodyType(
|
||||
float_t entityPhysicsGetGravityScale(
|
||||
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;
|
||||
return phys->gravityScale;
|
||||
}
|
||||
|
||||
void entityPhysicsSetGravityScale(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const float_t scale
|
||||
) {
|
||||
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
|
||||
assertNotNull(phys, "Failed to get physics component data");
|
||||
phys->gravityScale = scale;
|
||||
}
|
||||
|
||||
void entityPhysicsDispose(
|
||||
|
||||
@@ -122,12 +122,24 @@ bool_t entityPhysicsIsOnGround(
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the body type of the entity's physics body.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @return The body type (static, dynamic, kinematic).
|
||||
*/
|
||||
physicsbodytype_t entityPhysicsGetBodyType(
|
||||
const entityid_t entityId,
|
||||
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.
|
||||
* @param type The new body type.
|
||||
*/
|
||||
void entityPhysicsSetBodyType(
|
||||
const entityid_t entityId,
|
||||
@@ -136,17 +148,30 @@ void entityPhysicsSetBodyType(
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the body type of the entity's physics body.
|
||||
* Gets the gravity scale of the entity's physics body.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @return The body type of the physics body.
|
||||
* @return The gravity scale factor.
|
||||
*/
|
||||
physicsbodytype_t entityPhysicsGetBodyType(
|
||||
float_t entityPhysicsGetGravityScale(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the gravity scale of the entity's physics body.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param scale The new gravity scale factor.
|
||||
*/
|
||||
void entityPhysicsSetGravityScale(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const float_t scale
|
||||
);
|
||||
|
||||
/**
|
||||
* Releases the body slot back to PHYSICS_WORLD. Called automatically when
|
||||
* the component is disposed via the component system.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# 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
|
||||
entityscript.c
|
||||
)
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entityscript.h"
|
||||
#include "entity/component.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void entityScriptInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entityscript_t *script = (entityscript_t*)componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_SCRIPT
|
||||
);
|
||||
scriptContextInit(&script->scriptContext);
|
||||
|
||||
// Define script globals.
|
||||
char_t strScript[64];
|
||||
snprintf(strScript, sizeof(strScript), "ENTITY_ID = %d\n", entityId);
|
||||
errorret_t ret = scriptContextExec(&script->scriptContext, strScript);
|
||||
if(ret.code != ERROR_OK) {
|
||||
errorCatch(errorPrint(ret));
|
||||
assertUnreachable("Failed to set up script globals");
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t entityScriptExec(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const char_t *script
|
||||
) {
|
||||
entityscript_t *entityScript = (entityscript_t*)componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_SCRIPT
|
||||
);
|
||||
return scriptContextExec(&entityScript->scriptContext, script);
|
||||
}
|
||||
|
||||
errorret_t entityScriptExecAsset(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const char_t *assetName
|
||||
) {
|
||||
entityscript_t *entityScript = (entityscript_t*)componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_SCRIPT
|
||||
);
|
||||
return scriptContextExecFile(&entityScript->scriptContext, assetName);
|
||||
}
|
||||
|
||||
void entityScriptDispose(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entityscript_t *script = (entityscript_t*)componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_SCRIPT
|
||||
);
|
||||
scriptContextDispose(&script->scriptContext);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity/entitybase.h"
|
||||
#include "script/scriptcontext.h"
|
||||
|
||||
typedef struct {
|
||||
scriptcontext_t scriptContext;
|
||||
} entityscript_t;
|
||||
|
||||
/**
|
||||
* Initializes the script entity component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
*/
|
||||
void entityScriptInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Executes a script on the entity's script component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param script The script to execute.
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t entityScriptExec(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const char_t *script
|
||||
);
|
||||
|
||||
/**
|
||||
* Executes a script from an asset on the entity's script component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param assetName The name of the script asset to execute.
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t entityScriptExecAsset(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const char_t *assetName
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes of the script entity component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
*/
|
||||
void entityScriptDispose(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
@@ -10,15 +10,11 @@
|
||||
#include "entity/component/display/entitymesh.h"
|
||||
#include "entity/component/display/entitymaterial.h"
|
||||
#include "entity/component/physics/entityphysics.h"
|
||||
|
||||
// Name (Uppercase)
|
||||
// Structure
|
||||
// Field name (lowercase)
|
||||
// Init function (optional)
|
||||
// Dispose function (optional)
|
||||
#include "entity/component/script/entityscript.h"
|
||||
|
||||
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
||||
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
||||
X(MESH, entitymesh_t, mesh, entityMeshInit, entityMeshDispose)
|
||||
X(MESH, entitymesh_t, mesh, entityMeshInit, NULL)
|
||||
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
|
||||
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
|
||||
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
|
||||
X(SCRIPT, entityscript_t, script, entityScriptInit, entityScriptDispose)
|
||||
@@ -8,8 +8,8 @@
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#define ENTITY_COUNT_MAX 20
|
||||
#define ENTITY_COMPONENT_COUNT_MAX 8
|
||||
#define ENTITY_COUNT_MAX 64
|
||||
#define ENTITY_COMPONENT_COUNT_MAX 16
|
||||
|
||||
typedef uint8_t entityid_t;
|
||||
typedef uint8_t componentid_t;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "entitymanager.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "console/console.h"
|
||||
|
||||
entitymanager_t ENTITY_MANAGER;
|
||||
|
||||
@@ -19,8 +18,8 @@ void entityManagerInit(void) {
|
||||
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
|
||||
);
|
||||
|
||||
consolePrint(
|
||||
"Entity Manager size: %zu bytes (%.2f KB)",
|
||||
printf(
|
||||
"Entity Manager size is currently: %zu bytes (%.2f KB)\n",
|
||||
sizeof(entitymanager_t),
|
||||
sizeof(entitymanager_t) / 1024.0f
|
||||
);
|
||||
|
||||
+89
-76
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "event.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "console/console.h"
|
||||
|
||||
void eventInit(
|
||||
event_t *event,
|
||||
@@ -47,16 +46,19 @@ eventsub_t eventSubscribeUser(
|
||||
"Script event listener context cannot be NULL"
|
||||
);
|
||||
assertTrue(
|
||||
user.script.funcValue != 0,
|
||||
user.script.luaFunctionRef != LUA_NOREF,
|
||||
"Script event listener function reference is invalid"
|
||||
);
|
||||
} else {
|
||||
assertUnreachable("Unknown event listener type");
|
||||
}
|
||||
|
||||
|
||||
// Gen a new ID
|
||||
eventsub_t id = event->nextId++;
|
||||
// Did we wrap?
|
||||
assertTrue(event->nextId != 0, "Event subscription ID overflow");
|
||||
|
||||
// Append listener
|
||||
eventlistener_t *listener = &event->listenerArray[event->listenerCount++];
|
||||
memoryZero(listener, sizeof(eventlistener_t));
|
||||
listener->user = user;
|
||||
@@ -71,7 +73,7 @@ eventsub_t eventSubscribe(
|
||||
const eventcallback_t callback,
|
||||
const void *user
|
||||
) {
|
||||
return eventSubscribeUser(
|
||||
eventSubscribeUser(
|
||||
event,
|
||||
EVENT_TYPE_C,
|
||||
(eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } }
|
||||
@@ -80,34 +82,54 @@ eventsub_t eventSubscribe(
|
||||
|
||||
eventsub_t eventSubscribeScriptContext(
|
||||
event_t *event,
|
||||
scriptmanager_t *context,
|
||||
jerry_value_t funcValue
|
||||
scriptcontext_t *context,
|
||||
const int functionIndex
|
||||
) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
assertTrue(funcValue != 0, "Script function value is invalid");
|
||||
assertTrue(
|
||||
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;
|
||||
uint8_t i = 0;
|
||||
uint8_t i;
|
||||
i = 0;
|
||||
do {
|
||||
if(context->subscribedEvents[i] == event) {
|
||||
alreadySubbed = true;
|
||||
break;
|
||||
if(context->subscribedEvents[i] != event) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
} while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS);
|
||||
|
||||
if(context->subscribedEvents[i] == NULL) break;
|
||||
|
||||
alreadySubbed = true;
|
||||
break;
|
||||
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
|
||||
|
||||
if(!alreadySubbed) {
|
||||
i = 0;
|
||||
do {
|
||||
if(context->subscribedEvents[i] == NULL) {
|
||||
context->subscribedEvents[i] = event;
|
||||
break;
|
||||
if(context->subscribedEvents[i] != NULL) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
} while(i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS);
|
||||
|
||||
context->subscribedEvents[i] = event;
|
||||
break;
|
||||
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
|
||||
|
||||
assertTrue(
|
||||
i < SCRIPT_MANAGER_MAX_EVENT_SUBSCRIPTIONS,
|
||||
i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS,
|
||||
"Script context has reached maximum event subscriptions"
|
||||
);
|
||||
}
|
||||
@@ -115,12 +137,7 @@ eventsub_t eventSubscribeScriptContext(
|
||||
return eventSubscribeUser(
|
||||
event,
|
||||
EVENT_TYPE_SCRIPT,
|
||||
(eventuserdata_t){
|
||||
.script = {
|
||||
.context = context,
|
||||
.funcValue = funcValue
|
||||
}
|
||||
}
|
||||
(eventuserdata_t){ .script = scriptUser }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -130,34 +147,38 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) {
|
||||
|
||||
if(event->listenerCount == 0) return;
|
||||
|
||||
// Find listener
|
||||
uint16_t index = 0;
|
||||
do {
|
||||
if(event->listenerArray[index].id != id) {
|
||||
index++;
|
||||
continue;
|
||||
if(event->listenerArray[index].id == id) {
|
||||
// Found it, remove by swapping with last and reducing count
|
||||
event->listenerArray[index] = event->listenerArray[--event->listenerCount];
|
||||
return;
|
||||
}
|
||||
|
||||
if(event->listenerArray[index].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;
|
||||
index++;
|
||||
} 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 scriptmanager_t *ctx
|
||||
) {
|
||||
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
|
||||
assertNotNull(event, "Event cannot be NULL");
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
if(event->listenerCount == 0) return;
|
||||
|
||||
|
||||
uint16_t i = 0;
|
||||
do {
|
||||
eventlistener_t *listener = &event->listenerArray[i];
|
||||
@@ -168,6 +189,9 @@ void eventUnsubscribeScriptContext(
|
||||
i++;
|
||||
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);
|
||||
} while(i < event->listenerCount);
|
||||
}
|
||||
@@ -180,55 +204,44 @@ void eventInvoke(
|
||||
assertNotNull(event, "Event cannot be NULL");
|
||||
|
||||
if(event->listenerCount == 0) return;
|
||||
|
||||
|
||||
event->isInvoking = true;
|
||||
|
||||
eventdata_t data = {
|
||||
uint16_t i = 0;
|
||||
eventdata_t data ={
|
||||
.event = event,
|
||||
.eventParams = eventParams,
|
||||
};
|
||||
|
||||
uint16_t i = 0;
|
||||
do {
|
||||
eventlistener_t *listener = &event->listenerArray[i];
|
||||
|
||||
if(listener->type == EVENT_TYPE_C) {
|
||||
listener->user.c.callback(&data, listener->user.c);
|
||||
} else if(listener->type == EVENT_TYPE_SCRIPT) {
|
||||
jerry_value_t funcVal = listener->user.script.funcValue;
|
||||
assertNotNull((void *)(uintptr_t)funcVal, "Script function value is NULL");
|
||||
// Call Lua function
|
||||
lua_State *L = listener->user.script.context->luaState;
|
||||
assertNotNull(L, "Lua state in event listener cannot be NULL");
|
||||
|
||||
jerry_value_t callArgs[1];
|
||||
jerry_length_t argCount = 0;
|
||||
// Push function
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
|
||||
|
||||
if(eventParams != NULL) {
|
||||
callArgs[0] = jerry_object();
|
||||
jerry_object_set_native_ptr(
|
||||
callArgs[0], &JS_PTR_NATIVE_INFO, (void *)eventParams
|
||||
if(eventParams != NULL && metatableName != NULL) {
|
||||
lua_getmetatable(L, -1);
|
||||
luaL_getmetatable(L, metatableName);
|
||||
assertTrue(
|
||||
lua_rawequal(L, -1, -2),
|
||||
"Event parameter metatable does not match expected type"
|
||||
);
|
||||
argCount = 1;
|
||||
}
|
||||
|
||||
jerry_value_t result = jerry_call(
|
||||
funcVal, jerry_undefined(), callArgs, argCount
|
||||
);
|
||||
|
||||
if(argCount > 0) jerry_value_free(callArgs[0]);
|
||||
|
||||
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);
|
||||
// Call function with 1 arg, 0 return values
|
||||
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
// Log error but continue
|
||||
printf("Error invoking Lua event listener: %s\n", strErr);
|
||||
}
|
||||
|
||||
jerry_value_free(result);
|
||||
} else {
|
||||
assertUnreachable("Unknown event listener type");
|
||||
}
|
||||
@@ -236,4 +249,4 @@ void eventInvoke(
|
||||
} while(i < event->listenerCount);
|
||||
|
||||
event->isInvoking = false;
|
||||
}
|
||||
}
|
||||
@@ -83,8 +83,8 @@ eventsub_t eventSubscribe(
|
||||
*/
|
||||
eventsub_t eventSubscribeScriptContext(
|
||||
event_t *event,
|
||||
scriptmanager_t *context,
|
||||
jerry_value_t funcValue
|
||||
scriptcontext_t *context,
|
||||
const int functionIndex
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -101,10 +101,7 @@ void eventUnsubscribe(event_t *event, const eventsub_t subscription);
|
||||
* @param event The event to unsubscribe from.
|
||||
* @param context The script context whose listeners should be removed.
|
||||
*/
|
||||
void eventUnsubscribeScriptContext(
|
||||
event_t *event,
|
||||
const scriptmanager_t *ctx
|
||||
);
|
||||
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx);
|
||||
|
||||
/**
|
||||
* Invoke an event, calling all subscribed listeners. Optionally provide event
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "eventcallback.h"
|
||||
#include "script/scriptmanager.h"
|
||||
#include "script/scriptcontext.h"
|
||||
|
||||
typedef enum {
|
||||
EVENT_TYPE_C = 0,
|
||||
@@ -15,8 +15,8 @@ typedef enum {
|
||||
} eventtype_t;
|
||||
|
||||
typedef struct {
|
||||
scriptmanager_t *context;
|
||||
jerry_value_t funcValue;
|
||||
scriptcontext_t *context;
|
||||
int luaFunctionRef;
|
||||
} eventscript_t;
|
||||
|
||||
typedef struct eventc_s {
|
||||
@@ -27,4 +27,4 @@ typedef struct eventc_s {
|
||||
typedef union eventuserdata_u {
|
||||
eventscript_t script;
|
||||
eventc_t c;
|
||||
} eventuserdata_t;
|
||||
} eventuserdata_t;
|
||||
+28
-34
@@ -95,27 +95,27 @@ void inputUpdate(void) {
|
||||
if(TIME.dynamicUpdate) return;
|
||||
#endif
|
||||
|
||||
// if(INPUT.eventPressed.listenerCount > 0) {
|
||||
// action = &INPUT.actions[0];
|
||||
// do {
|
||||
// if(inputPressed(action->action)) {
|
||||
// inputevent_t inputEvent = { .action = action->action };
|
||||
// eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt");
|
||||
// }
|
||||
// action++;
|
||||
// } while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||
// }
|
||||
if(INPUT.eventPressed.listenerCount > 0) {
|
||||
action = &INPUT.actions[0];
|
||||
do {
|
||||
if(inputPressed(action->action)) {
|
||||
inputevent_t inputEvent = { .action = action->action };
|
||||
eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt");
|
||||
}
|
||||
action++;
|
||||
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||
}
|
||||
|
||||
// if(INPUT.eventReleased.listenerCount > 0) {
|
||||
// action = &INPUT.actions[0];
|
||||
// do {
|
||||
// if(inputReleased(action->action)) {
|
||||
// inputevent_t inputEvent = { .action = action->action };
|
||||
// eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt");
|
||||
// }
|
||||
// action++;
|
||||
// } while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||
// }
|
||||
if(INPUT.eventReleased.listenerCount > 0) {
|
||||
action = &INPUT.actions[0];
|
||||
do {
|
||||
if(inputReleased(action->action)) {
|
||||
inputevent_t inputEvent = { .action = action->action };
|
||||
eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt");
|
||||
}
|
||||
action++;
|
||||
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||
}
|
||||
}
|
||||
|
||||
float_t inputGetCurrentValue(const inputaction_t action) {
|
||||
@@ -171,16 +171,6 @@ float_t inputAxis(const inputaction_t neg, const inputaction_t pos) {
|
||||
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) {
|
||||
assertTrue(
|
||||
act < INPUT_ACTION_COUNT,
|
||||
@@ -189,15 +179,19 @@ void inputBind(const inputbutton_t button, const inputaction_t act) {
|
||||
assertTrue(act != INPUT_ACTION_NULL, "Cannot bind to NULL action");
|
||||
|
||||
// Get the button data for this button.
|
||||
inputbuttondata_t *data = inputButtonGetData(button);
|
||||
assertNotNull(data, "Input button not found");
|
||||
inputbuttondata_t *data = INPUT_BUTTON_DATA;
|
||||
do {
|
||||
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.
|
||||
data->action = act;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float_t inputDeadzone(const float_t rawValue, const float_t deadzone) {
|
||||
if(rawValue < deadzone) return 0.0f;
|
||||
return (rawValue - deadzone) / (1.0f - deadzone);
|
||||
|
||||
@@ -5,7 +5,6 @@ LEFT,
|
||||
RIGHT,
|
||||
ACCEPT,
|
||||
CANCEL,
|
||||
RAGEQUIT,
|
||||
CONSOLE,
|
||||
RAGEQUIT
|
||||
POINTERX,
|
||||
POINTERY,
|
||||
|
@@ -120,22 +120,6 @@ bool_t inputReleased(const inputaction_t action);
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
#include "assert/assert.h"
|
||||
#include "util/string.h"
|
||||
|
||||
inputaction_t inputActionGetByName(const char_t *name) {
|
||||
assertNotNull(name, "name must not be NULL");
|
||||
// inputaction_t inputActionGetByName(const char_t *name) {
|
||||
// assertNotNull(name, "name must not be NULL");
|
||||
|
||||
for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||
if(INPUT_ACTION_IDS[i] == NULL) continue;
|
||||
if(stringCompareInsensitive(INPUT_ACTION_IDS[i], name) != 0) continue;
|
||||
return i;
|
||||
}
|
||||
// for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||
// if(INPUT_ACTION_IDS[i] == NULL) continue;
|
||||
// if(stringCompareInsensitive(INPUT_ACTION_IDS[i], name) != 0) continue;
|
||||
// return i;
|
||||
// }
|
||||
|
||||
return INPUT_ACTION_COUNT;
|
||||
}
|
||||
// return INPUT_ACTION_COUNT;
|
||||
// }
|
||||
@@ -26,4 +26,4 @@ typedef struct {
|
||||
* @param name The name of the input action.
|
||||
* @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,7 +9,6 @@
|
||||
#include "input.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/string.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
inputbutton_t inputButtonGetByName(const char_t *name) {
|
||||
assertNotNull(name, "name must not be NULL");
|
||||
@@ -27,16 +26,4 @@ inputbutton_t inputButtonGetByName(const char_t *name) {
|
||||
|
||||
float_t inputButtonGetValue(const inputbutton_t 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,12 +96,4 @@ inputbutton_t inputButtonGetByName(const char_t *name);
|
||||
* @param button The input button.
|
||||
* @return The current value of the input button (0.0f to 1.0f).
|
||||
*/
|
||||
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);
|
||||
float_t inputButtonGetValue(const inputbutton_t button);
|
||||
@@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
network.c
|
||||
networkinfo.c
|
||||
)
|
||||
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "network.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
#include "log/log.h"
|
||||
|
||||
network_t NETWORK;
|
||||
|
||||
errorret_t networkInit() {
|
||||
memoryZero(&NETWORK, sizeof(network_t));
|
||||
|
||||
NETWORK.errorState.code = ERROR_OK;
|
||||
NETWORK.onDisconnect = NULL;
|
||||
|
||||
return networkPlatformInit();
|
||||
}
|
||||
|
||||
errorret_t networkUpdate() {
|
||||
errorChain(networkPlatformUpdate());
|
||||
|
||||
if(NETWORK.state == NETWORK_STATE_CONNECTED && !networkIsConnected()) {
|
||||
NETWORK.state = NETWORK_STATE_DISCONNECTED;
|
||||
|
||||
if(NETWORK.onDisconnect) {
|
||||
errorret_t ret;
|
||||
if(NETWORK.errorState.code == ERROR_OK) {
|
||||
ret = errorThrowImpl(
|
||||
&NETWORK.errorState,
|
||||
ERROR_NOT_OK,
|
||||
__FILE__, __func__, __LINE__,
|
||||
"Network connection lost"
|
||||
);
|
||||
} else {
|
||||
ret.code = NETWORK.errorState.code;
|
||||
ret.state = &NETWORK.errorState;
|
||||
}
|
||||
NETWORK.onDisconnect(ret, NETWORK.disconnectUser);
|
||||
}
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
bool_t networkIsConnected() {
|
||||
return networkPlatformIsConnected();
|
||||
}
|
||||
|
||||
void networkRequestConnection(
|
||||
void (*onConnected)(void *user),
|
||||
void (*onFailed)(errorret_t error, void *user),
|
||||
void (*onDisconnect)(errorret_t error, void *user),
|
||||
void *user
|
||||
) {
|
||||
assertNotNull(onConnected, "onConnected callback must not be null");
|
||||
assertNotNull(onFailed, "onFailed callback must not be null");
|
||||
assertNotNull(onDisconnect, "onDisconnect callback must not be null");
|
||||
|
||||
NETWORK.state = NETWORK_STATE_CONNECTING;
|
||||
NETWORK.onDisconnect = onDisconnect;
|
||||
NETWORK.disconnectUser = user;
|
||||
|
||||
#ifndef networkPlatformRequestConnection
|
||||
// This is a platform cannot be requested to go online, this would basically
|
||||
// be for platforms like Linux or Windows where the OS is responsible for
|
||||
// maintaining the network connection.
|
||||
|
||||
if(networkIsConnected()) {
|
||||
NETWORK.state = NETWORK_STATE_CONNECTED;
|
||||
onConnected(user);
|
||||
} else {
|
||||
errorret_t ret = errorThrowImpl(
|
||||
&NETWORK.errorState,
|
||||
ERROR_NOT_OK,
|
||||
__FILE__, __func__, __LINE__,
|
||||
"No network connection available"
|
||||
);
|
||||
onFailed(ret, user);
|
||||
}
|
||||
#else
|
||||
networkPlatformRequestConnection(onConnected, onFailed, onDisconnect, user);
|
||||
#endif
|
||||
}
|
||||
|
||||
void networkRequestDisconnection(
|
||||
void (*onComplete)(void *user),
|
||||
void *user
|
||||
) {
|
||||
assertNotNull(onComplete, "onComplete callback must not be null");
|
||||
NETWORK.state = NETWORK_STATE_DISCONNECTING;
|
||||
|
||||
#ifndef networkPlatformRequestDisconnection
|
||||
NETWORK.state = NETWORK_STATE_DISCONNECTED;
|
||||
onComplete(user);
|
||||
#else
|
||||
networkPlatformRequestDisconnection(onComplete, user);
|
||||
#endif
|
||||
}
|
||||
|
||||
void networkDisconnectedDuringDispose(void *u) {
|
||||
logDebug("Network disconnected during dispose\n");
|
||||
}
|
||||
|
||||
errorret_t networkDispose() {
|
||||
if(NETWORK.state == NETWORK_STATE_CONNECTED) {
|
||||
networkRequestDisconnection(networkDisconnectedDuringDispose, NULL);
|
||||
}
|
||||
|
||||
errorChain(networkPlatformDispose());
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "network/networkplatform.h"
|
||||
#ifndef networkPlatformInit
|
||||
#error "networkPlatformInit must be defined"
|
||||
#endif
|
||||
#ifndef networkPlatformUpdate
|
||||
#error "networkPlatformUpdate must be defined"
|
||||
#endif
|
||||
#ifndef networkPlatformDispose
|
||||
#error "networkPlatformDispose must be defined"
|
||||
#endif
|
||||
#ifndef networkPlatformIsConnected
|
||||
#error "networkPlatformIsConnected must be defined"
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
NETWORK_STATE_DISCONNECTED,
|
||||
NETWORK_STATE_CONNECTING,
|
||||
NETWORK_STATE_CONNECTED,
|
||||
NETWORK_STATE_DISCONNECTING,
|
||||
} networkstate_t;
|
||||
|
||||
typedef struct {
|
||||
networkplatform_t platform;
|
||||
errorstate_t errorState;
|
||||
networkstate_t state;
|
||||
|
||||
void (*onDisconnect)(errorret_t error, void *user);
|
||||
void *disconnectUser;
|
||||
} network_t;
|
||||
|
||||
extern network_t NETWORK;
|
||||
|
||||
/**
|
||||
* Initializes the network system. This will NOT connect to the network.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t networkInit();
|
||||
|
||||
/**
|
||||
* Updates the network manager, dispatching any completed async request
|
||||
* callbacks on the main thread.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t networkUpdate();
|
||||
|
||||
/**
|
||||
* Disposes of the network manager. This will NOT disconnect from the network.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t networkDispose();
|
||||
|
||||
/**
|
||||
* Returns true if the system is connected to AN network, this doesn't mean that
|
||||
* requests will succeed, doesn't mean there's internet, just that we could
|
||||
* possibly make network requests. If this is false, this usually means
|
||||
* something like;
|
||||
* - A network cable is not connnected
|
||||
* - No Wi-Fi Connection has been established
|
||||
* - No IP Address has been assigned
|
||||
*
|
||||
* That kinda stuff.
|
||||
*
|
||||
* In future I will probably have REASONS for why it's not connected, for
|
||||
* example;
|
||||
* - On PSP you need to "request" network access
|
||||
* - On GameCube, network settings need to be defined, including DHCP, etc.
|
||||
* - On Windows, this may require additional permissions
|
||||
*
|
||||
* @return True if some network connection is detected.
|
||||
*/
|
||||
bool_t networkIsConnected();
|
||||
|
||||
/**
|
||||
* See networkIsConnected for a bit more info, but this is for some
|
||||
* platforms (mainly PSP) to request the system to start doing networking.
|
||||
*
|
||||
* You should only call this once and assume that it is "pending" until either
|
||||
* onComplete or onFailed is invoked. If you call this twice it is undefined
|
||||
* behavior.
|
||||
*
|
||||
* onDisconnect must be provided, and is called whenever the network is lost
|
||||
* after a successful connection. This will NOT be called if disconnect is
|
||||
* manually triggered, but WILL if an error occurs, or a network stack bug, etc.
|
||||
*
|
||||
* @param onConnected Callback to invoke when the network is connected.
|
||||
* @param onFailed Callback to invoke if the network connection fails.
|
||||
* @param onDisconnect Called after a successful connection, when disconnected.
|
||||
* @param user User data to pass to the callbacks.
|
||||
*/
|
||||
void networkRequestConnection(
|
||||
void (*onConnected)(void *user),
|
||||
void (*onFailed)(errorret_t error, void *user),
|
||||
void (*onDisconnect)(errorret_t error, void *user),
|
||||
void *user
|
||||
);
|
||||
|
||||
/**
|
||||
* Requests the system to disconnect from the network. This is basically just
|
||||
* for PSP, but I guess it could be used on other platforms in future if they
|
||||
* have some kind of "network connection mode" that needs to be exited.
|
||||
*
|
||||
* You should only call this once and assume that it is "pending" until
|
||||
* onComplete is invoked. If you call this twice it is undefined behavior.
|
||||
*
|
||||
* If it fails, you still get onComplete called, but may fail if you try to
|
||||
* reconnect later unfortunately.
|
||||
*
|
||||
* @param onComplete Callback to invoke when the network is disconnected.
|
||||
* @param user User data to pass to the callback.
|
||||
*/
|
||||
void networkRequestDisconnection(
|
||||
void (*onComplete)(void *user),
|
||||
void *user
|
||||
);
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "network.h"
|
||||
#include "networkinfo.h"
|
||||
#include "network/networkplatform.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
#ifndef networkPlatformGetInfo
|
||||
#error "networkPlatformGetInfo must be defined"
|
||||
#endif
|
||||
|
||||
networkinfo_t networkGetInfo() {
|
||||
assertTrue(
|
||||
NETWORK.state == NETWORK_STATE_CONNECTED,
|
||||
"networkGetInfo called when not connected"
|
||||
);
|
||||
|
||||
return networkPlatformGetInfo();
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#define NETWORK_INFO_IPV4_DNS_COUNT_MAX 2
|
||||
#define NETWORK_INFO_IPV4_OCTET_COUNT 4
|
||||
#define NETWORK_INFO_IPV6_OCTET_COUNT 16
|
||||
#define NETWORK_INFO_IPV6_DNS_COUNT_MAX 2
|
||||
#define NETWORK_INFO_FORMAT_IPV4 "%u.%u.%u.%u"
|
||||
#define NETWORK_INFO_FORMAT_IPV6 \
|
||||
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x"
|
||||
|
||||
typedef enum {
|
||||
NETWORK_TYPE_IPV4,
|
||||
#ifdef DUSK_NETWORK_IPV6
|
||||
NETWORK_TYPE_IPV6,
|
||||
#endif
|
||||
} networktype_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t ip[NETWORK_INFO_IPV4_OCTET_COUNT];
|
||||
// uint8_t subnet[NETWORK_INFO_IPV4_OCTET_COUNT];
|
||||
// uint8_t gateway[NETWORK_INFO_IPV4_OCTET_COUNT];
|
||||
// uint8_t dns[NETWORK_INFO_IPV4_OCTET_COUNT][NETWORK_INFO_IPV4_DNS_COUNT_MAX];
|
||||
} networkinfoipv4_t;
|
||||
|
||||
#ifdef DUSK_NETWORK_IPV6
|
||||
typedef struct {
|
||||
uint8_t ip[NETWORK_INFO_IPV6_OCTET_COUNT];
|
||||
// uint8_t subnet[NETWORK_INFO_IPV6_OCTET_COUNT];
|
||||
// uint8_t gateway[NETWORK_INFO_IPV6_OCTET_COUNT];
|
||||
// uint8_t dns[NETWORK_INFO_IPV6_OCTET_COUNT][NETWORK_INFO_IPV6_DNS_COUNT_MAX];
|
||||
} networkinfoipv6_t;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
networktype_t type;
|
||||
union {
|
||||
networkinfoipv4_t ipv4;
|
||||
#ifdef DUSK_NETWORK_IPV6
|
||||
networkinfoipv6_t ipv6;
|
||||
#endif
|
||||
};
|
||||
} networkinfo_t;
|
||||
|
||||
/**
|
||||
* Returns the network information for the currently active network connection.
|
||||
*
|
||||
* @return Network information for the currently active network connection.
|
||||
*/
|
||||
networkinfo_t networkGetInfo();
|
||||
+57
-275
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2026 Dominic Masters
|
||||
//
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
@@ -12,19 +12,12 @@
|
||||
#include "entity/entitymanager.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/mesh/cube.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/text/text.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;
|
||||
|
||||
errorret_t sceneInit(void) {
|
||||
memoryZero(&SCENE, sizeof(scene_t));
|
||||
SCENE.scriptRef = SCENE_SCRIPT_REF_NONE;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -34,30 +27,31 @@ errorret_t sceneUpdate(void) {
|
||||
errorOk();
|
||||
}
|
||||
#endif
|
||||
|
||||
if(stringCompare(SCENE.sceneNext, SCENE.sceneCurrent) != 0) {
|
||||
errorChain(sceneSetImmediate(SCENE.sceneNext));
|
||||
}
|
||||
|
||||
if(SCENE.sceneActive) {
|
||||
errorChain(moduleSceneCall("update"));
|
||||
}
|
||||
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
dusktimeepoch_t LAST;
|
||||
|
||||
errorret_t sceneRender(void) {
|
||||
// Get Cameras
|
||||
entityid_t camEnts[ENTITY_COUNT_MAX];
|
||||
componentid_t camComps[ENTITY_COUNT_MAX];
|
||||
entityid_t camCount = componentGetEntitiesWithComponent(
|
||||
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;
|
||||
errorChain(shaderBind(&SHADER_UNLIT));
|
||||
|
||||
// For each camera
|
||||
for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
|
||||
entityid_t camEnt = camEnts[camIndex];
|
||||
componentid_t camComp = camComps[camIndex];
|
||||
@@ -70,271 +64,59 @@ errorret_t sceneRender(void) {
|
||||
entityCameraGetProjection(camEnt, camComp, proj);
|
||||
entityPositionGetTransform(camEnt, camPos, view);
|
||||
|
||||
// For each entity
|
||||
for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) {
|
||||
// Does this entity have a material?
|
||||
componentid_t matComp = entityGetComponent(
|
||||
entityId, COMPONENT_TYPE_MATERIAL
|
||||
);
|
||||
if(matComp != 0xFF) {
|
||||
// Yes, get the mesh
|
||||
componentid_t meshComp = entityGetComponent(
|
||||
entityId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
// For each mesh.
|
||||
for(entityid_t meshIndex = 0; meshIndex < meshCount; meshIndex++) {
|
||||
entityid_t meshEnt = meshEnts[meshIndex];
|
||||
|
||||
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));
|
||||
componentid_t meshComp = meshComps[meshIndex];
|
||||
mesh_t *mesh = entityMeshGetMesh(meshEnt, meshComp);
|
||||
if(mesh == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// No, in future there may be other renderable types.
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
errorOk();
|
||||
|
||||
// if(camCount > 0) {
|
||||
// // For each entity
|
||||
// for(entityid_t entityId = 0; entityId < ENTITY_COUNT_MAX; entityId++) {
|
||||
|
||||
// }
|
||||
|
||||
// entityid_t meshEnts[ENTITY_COUNT_MAX];
|
||||
// componentid_t meshComps[ENTITY_COUNT_MAX];
|
||||
// entityid_t meshCount = componentGetEntitiesWithComponent(
|
||||
// COMPONENT_TYPE_MESH, meshEnts, meshComps
|
||||
// );
|
||||
|
||||
// if(meshCount > 0) {
|
||||
// errorChain(shaderBind(&SHADER_UNLIT));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
errorret_t sceneSet(const char_t *script) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void sceneSet(const char_t *scene) {
|
||||
stringCopy(
|
||||
SCENE.sceneNext,
|
||||
scene == NULL ? "" : scene,
|
||||
ASSET_FILE_PATH_MAX
|
||||
);
|
||||
}
|
||||
void sceneDispose(void) {
|
||||
|
||||
errorret_t sceneDispose(void) {
|
||||
errorChain(moduleSceneCall("dispose"));
|
||||
errorOk();
|
||||
}
|
||||
}
|
||||
+18
-38
@@ -1,68 +1,48 @@
|
||||
/**
|
||||
* 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 "asset/assetfile.h"
|
||||
#include "event/event.h"
|
||||
|
||||
#define SCENE_EVENT_UPDATE_MAX 16
|
||||
#include "error/error.h"
|
||||
|
||||
typedef struct {
|
||||
bool_t sceneActive;
|
||||
jerry_value_t scriptRef;
|
||||
char_t sceneCurrent[ASSET_FILE_PATH_MAX];
|
||||
char_t sceneNext[ASSET_FILE_PATH_MAX];
|
||||
void *nothing;
|
||||
} scene_t;
|
||||
|
||||
extern scene_t SCENE;
|
||||
|
||||
/** Sentinel value meaning no scene script is loaded. */
|
||||
#define SCENE_SCRIPT_REF_NONE ((jerry_value_t)0)
|
||||
|
||||
/**
|
||||
* Initializes the scene manager.
|
||||
*
|
||||
* @return Any error state that happened.
|
||||
* Initialize the scene subsystem.
|
||||
*
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t sceneInit(void);
|
||||
|
||||
/**
|
||||
* Ticks the scene manager; may call the scene's update method.
|
||||
*
|
||||
* @return Any error state that happened.
|
||||
* Update the current scene.
|
||||
*
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t sceneUpdate(void);
|
||||
|
||||
/**
|
||||
* Renders the scene.
|
||||
*
|
||||
* @return Any error state that happened.
|
||||
* Render the current scene.
|
||||
*
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t sceneRender(void);
|
||||
|
||||
/**
|
||||
* Immediately switches scenes, disposing the current one first.
|
||||
*
|
||||
* @param scene Scene to switch to (asset file path).
|
||||
* @return Any error state that happened.
|
||||
* Set the current scene by script name.
|
||||
*
|
||||
* @param script The script name of the scene to set.
|
||||
*/
|
||||
errorret_t sceneSetImmediate(const char_t *scene);
|
||||
errorret_t sceneSet(const char_t *script);
|
||||
|
||||
/**
|
||||
* Requests a scene change on the next safe opportunity.
|
||||
*
|
||||
* @param scene Which scene to set.
|
||||
* Dispose of the scene subsystem.
|
||||
*/
|
||||
void sceneSet(const char_t *scene);
|
||||
|
||||
/**
|
||||
* Disposes of the current scene.
|
||||
*
|
||||
* @return Any error state that happened.
|
||||
*/
|
||||
errorret_t sceneDispose(void);
|
||||
void sceneDispose(void);
|
||||
@@ -7,7 +7,9 @@
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
scriptmanager.c
|
||||
scriptproto.c
|
||||
scriptcontext.c
|
||||
scriptmodule.c
|
||||
)
|
||||
|
||||
# Subdirectories
|
||||
# Subdirectories
|
||||
add_subdirectory(module)
|
||||
@@ -0,0 +1,15 @@
|
||||
# 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)
|
||||
add_subdirectory(entity)
|
||||
@@ -1,67 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# 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
|
||||
)
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* 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,158 +1,55 @@
|
||||
/**
|
||||
* 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 "display/color.h"
|
||||
#include "time/time.h"
|
||||
#include "script/scriptproto.h"
|
||||
#include "script/scriptcontext.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(
|
||||
const jerry_call_info_t *callInfo
|
||||
) {
|
||||
return (color_t*)scriptProtoGetValue(
|
||||
&MODULE_COLOR_PROTO, callInfo->this_value
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Lua function to create a color.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values.
|
||||
*/
|
||||
int moduleColorFuncColor(lua_State *L);
|
||||
|
||||
moduleBaseFunction(moduleColorGetR) {
|
||||
color_t *c = moduleColorGet(callInfo);
|
||||
return c ? jerry_number(c->r) : jerry_undefined();
|
||||
}
|
||||
/**
|
||||
* Index function for the color structure.
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values.
|
||||
*/
|
||||
int moduleColorIndex(lua_State *L);
|
||||
|
||||
moduleBaseFunction(moduleColorGetG) {
|
||||
color_t *c = moduleColorGet(callInfo);
|
||||
return c ? jerry_number(c->g) : jerry_undefined();
|
||||
}
|
||||
/**
|
||||
* New index function for the color structure.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values.
|
||||
*/
|
||||
int moduleColorNewIndex(lua_State *L);
|
||||
|
||||
moduleBaseFunction(moduleColorGetB) {
|
||||
color_t *c = moduleColorGet(callInfo);
|
||||
return c ? jerry_number(c->b) : jerry_undefined();
|
||||
}
|
||||
/**
|
||||
* Color to string method for script
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values.
|
||||
*/
|
||||
int moduleColorToString(lua_State *L);
|
||||
|
||||
moduleBaseFunction(moduleColorGetA) {
|
||||
color_t *c = moduleColorGet(callInfo);
|
||||
return c ? jerry_number(c->a) : jerry_undefined();
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleColorSetR) {
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* Lua function to create a rainbow color based on time.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values.
|
||||
*/
|
||||
int moduleColorRainbow(lua_State *L);
|
||||
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* 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);
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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,74 +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/display/modulecolor.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "script/scriptcontext.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) {
|
||||
return jerry_number(SCREEN.width);
|
||||
}
|
||||
/**
|
||||
* Gets the current screen width.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Count of return values.
|
||||
*/
|
||||
int moduleScreenGetWidth(lua_State *L);
|
||||
|
||||
moduleBaseFunction(moduleScreenGetHeight) {
|
||||
return jerry_number(SCREEN.height);
|
||||
}
|
||||
/**
|
||||
* Gets the current screen height.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Count of return values.
|
||||
*/
|
||||
int moduleScreenGetHeight(lua_State *L);
|
||||
|
||||
moduleBaseFunction(moduleScreenGetAspect) {
|
||||
return jerry_number(SCREEN.aspect);
|
||||
}
|
||||
|
||||
moduleBaseFunction(moduleScreenGetBackground) {
|
||||
return moduleColorMakeObject(SCREEN.background);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/**
|
||||
* Sets the screen background color.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Count of return values.
|
||||
*/
|
||||
int moduleScreenSetBackground(lua_State *L);
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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();
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -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/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);
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* 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,
|
||||
#if MESH_ENABLE_COLOR
|
||||
color == NULL ? COLOR_WHITE : *color,
|
||||
#endif
|
||||
&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;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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);
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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);
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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);
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 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,16 @@
|
||||
# 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
|
||||
moduleentity.c
|
||||
moduleentityposition.c
|
||||
moduleentitycamera.c
|
||||
moduleentitymesh.c
|
||||
moduleentitymaterial.c
|
||||
moduleentityphysics.c
|
||||
moduleentityscript.c
|
||||
)
|
||||
@@ -1,244 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
* 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
|
||||
);
|
||||
}
|
||||
@@ -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/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
|
||||
);
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
/**
|
||||
* 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,108 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduleentity.h"
|
||||
#include "assert/assert.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "entity/entity.h"
|
||||
#include "entity/component.h"
|
||||
|
||||
void moduleEntity(scriptcontext_t *ctx) {
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
lua_State *l = ctx->luaState;
|
||||
|
||||
// Component type constants
|
||||
lua_pushnumber(l, COMPONENT_TYPE_POSITION);
|
||||
lua_setglobal(l, "COMPONENT_TYPE_POSITION");
|
||||
|
||||
lua_pushnumber(l, COMPONENT_TYPE_CAMERA);
|
||||
lua_setglobal(l, "COMPONENT_TYPE_CAMERA");
|
||||
|
||||
lua_pushnumber(l, COMPONENT_TYPE_MESH);
|
||||
lua_setglobal(l, "COMPONENT_TYPE_MESH");
|
||||
|
||||
lua_pushnumber(l, COMPONENT_TYPE_MATERIAL);
|
||||
lua_setglobal(l, "COMPONENT_TYPE_MATERIAL");
|
||||
|
||||
lua_pushnumber(l, COMPONENT_TYPE_PHYSICS);
|
||||
lua_setglobal(l, "COMPONENT_TYPE_PHYSICS");
|
||||
|
||||
lua_pushnumber(l, COMPONENT_TYPE_SCRIPT);
|
||||
lua_setglobal(l, "COMPONENT_TYPE_SCRIPT");
|
||||
|
||||
// Entity functions
|
||||
lua_register(l, "entityCreate", moduleEntityCreate);
|
||||
lua_register(l, "entityAddComponent", moduleEntityAddComponent);
|
||||
lua_register(l, "entityGetComponent", moduleEntityGetComponent);
|
||||
lua_register(l, "entityDispose", moduleEntityDispose);
|
||||
}
|
||||
|
||||
int moduleEntityCreate(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
entityid_t id = entityManagerAdd();
|
||||
entityInit(id);
|
||||
lua_pushnumber(L, id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityAddComponent(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityAddComponent: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityAddComponent: componentType must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
|
||||
componenttype_t type = (componenttype_t)lua_tonumber(L, 2);
|
||||
componentid_t compId = entityAddComponent(entityId, type);
|
||||
lua_pushnumber(L, compId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityGetComponent(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityGetComponent: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityGetComponent: componentType must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
|
||||
componenttype_t type = (componenttype_t)lua_tonumber(L, 2);
|
||||
componentid_t compId = entityGetComponent(entityId, type);
|
||||
|
||||
if(compId == 0xFF) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushnumber(L, compId);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityDispose(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityDispose: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
|
||||
entityDispose(entityId);
|
||||
return 0;
|
||||
}
|
||||
@@ -6,200 +6,39 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/module/modulebase.h"
|
||||
#include "script/scriptproto.h"
|
||||
#include "entity/entity.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "script/scriptcontext.h"
|
||||
|
||||
#include "component/moduleentityposition.h"
|
||||
#include "component/moduleentitycamera.h"
|
||||
#include "component/moduleentitymesh.h"
|
||||
#include "component/moduleentitymaterial.h"
|
||||
#include "component/moduleentityphysics.h"
|
||||
/**
|
||||
* Registers the entity module within the given script context.
|
||||
* Provides: entityCreate, entityAddComponent, entityGetComponent, entityDispose
|
||||
* and all COMPONENT_TYPE_* constants.
|
||||
*
|
||||
* @param ctx The script context to register the module in.
|
||||
*/
|
||||
void moduleEntity(scriptcontext_t *ctx);
|
||||
|
||||
typedef struct {
|
||||
entityid_t id;
|
||||
} entityscript_t;
|
||||
/**
|
||||
* Lua binding: reserves and initializes a new entity.
|
||||
* Returns: entityId (integer)
|
||||
*/
|
||||
int moduleEntityCreate(lua_State *L);
|
||||
|
||||
static scriptproto_t MODULE_ENTITY_PROTO;
|
||||
/**
|
||||
* Lua binding: adds a component of the given type to an entity.
|
||||
* Args: entityId (integer), componentType (integer)
|
||||
* Returns: componentId (integer)
|
||||
*/
|
||||
int moduleEntityAddComponent(lua_State *L);
|
||||
|
||||
// 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);
|
||||
}
|
||||
/**
|
||||
* Lua binding: gets the component ID for the given type on an entity.
|
||||
* Args: entityId (integer), componentType (integer)
|
||||
* Returns: componentId (integer), or nil if the entity lacks the component
|
||||
*/
|
||||
int moduleEntityGetComponent(lua_State *L);
|
||||
|
||||
// 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
|
||||
}
|
||||
/**
|
||||
* Lua binding: disposes of the entity with the given ID.
|
||||
* Args: entityId (integer)
|
||||
*/
|
||||
int moduleEntityDispose(lua_State *L);
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduleentitycamera.h"
|
||||
#include "assert/assert.h"
|
||||
#include "entity/component/display/entitycamera.h"
|
||||
|
||||
void moduleEntityCamera(scriptcontext_t *ctx) {
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
lua_State *l = ctx->luaState;
|
||||
|
||||
// Projection type constants
|
||||
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE);
|
||||
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_PERSPECTIVE");
|
||||
|
||||
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED);
|
||||
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_PERSPECTIVE_FLIPPED");
|
||||
|
||||
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC);
|
||||
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_ORTHOGRAPHIC");
|
||||
|
||||
// Camera functions
|
||||
lua_register(l, "entityCameraGetZNear", moduleEntityCameraGetZNear);
|
||||
lua_register(l, "entityCameraSetZNear", moduleEntityCameraSetZNear);
|
||||
lua_register(l, "entityCameraGetZFar", moduleEntityCameraGetZFar);
|
||||
lua_register(l, "entityCameraSetZFar", moduleEntityCameraSetZFar);
|
||||
lua_register(l, "entityCameraGetProjType", moduleEntityCameraGetProjType);
|
||||
lua_register(l, "entityCameraSetProjType", moduleEntityCameraSetProjType);
|
||||
lua_register(l, "entityCameraGetFov", moduleEntityCameraGetFov);
|
||||
lua_register(l, "entityCameraSetFov", moduleEntityCameraSetFov);
|
||||
lua_register(l, "entityCameraSetOrthographic", moduleEntityCameraSetOrthographic);
|
||||
}
|
||||
|
||||
int moduleEntityCameraGetZNear(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraGetZNear: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraGetZNear: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
lua_pushnumber(L, entityCameraGetZNear(eid, cid));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityCameraSetZNear(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraSetZNear: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraSetZNear: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 3)) {
|
||||
luaL_error(L, "entityCameraSetZNear: zNear must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
float_t zNear = (float_t)lua_tonumber(L, 3);
|
||||
entityCameraSetZNear(eid, cid, zNear);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int moduleEntityCameraGetZFar(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraGetZFar: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraGetZFar: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
lua_pushnumber(L, entityCameraGetZFar(eid, cid));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityCameraSetZFar(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraSetZFar: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraSetZFar: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 3)) {
|
||||
luaL_error(L, "entityCameraSetZFar: zFar must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
float_t zFar = (float_t)lua_tonumber(L, 3);
|
||||
entityCameraSetZFar(eid, cid, zFar);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int moduleEntityCameraGetProjType(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraGetProjType: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraGetProjType: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
lua_pushnumber(L, entityCameraGetProjType(eid, cid));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityCameraSetProjType(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraSetProjType: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraSetProjType: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 3)) {
|
||||
luaL_error(L, "entityCameraSetProjType: projType must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
entitycameraprojectiontype_t type = (entitycameraprojectiontype_t)lua_tonumber(L, 3);
|
||||
entityCameraSetProjType(eid, cid, type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int moduleEntityCameraGetFov(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraGetFov: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraGetFov: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
lua_pushnumber(L, entityCameraGetFov(eid, cid));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityCameraSetFov(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraSetFov: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraSetFov: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 3)) {
|
||||
luaL_error(L, "entityCameraSetFov: fov must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
float_t fov = (float_t)lua_tonumber(L, 3);
|
||||
entityCameraSetFov(eid, cid, fov);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int moduleEntityCameraSetOrthographic(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityCameraSetOrthographic: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityCameraSetOrthographic: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 3) || !lua_isnumber(L, 4) ||
|
||||
!lua_isnumber(L, 5) || !lua_isnumber(L, 6)) {
|
||||
luaL_error(L, "entityCameraSetOrthographic: left/right/top/bottom must be numbers");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
float_t left = (float_t)lua_tonumber(L, 3);
|
||||
float_t right = (float_t)lua_tonumber(L, 4);
|
||||
float_t top = (float_t)lua_tonumber(L, 5);
|
||||
float_t bottom = (float_t)lua_tonumber(L, 6);
|
||||
entityCameraSetOrthographic(eid, cid, left, right, top, bottom);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* 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 entitycamera module within the given script context.
|
||||
* Provides get/set functions for znear, zfar, and projection type constants.
|
||||
*
|
||||
* @param ctx The script context to register the module in.
|
||||
*/
|
||||
void moduleEntityCamera(scriptcontext_t *ctx);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraGetZNear(entityId, componentId) -> number
|
||||
*/
|
||||
int moduleEntityCameraGetZNear(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraSetZNear(entityId, componentId, number)
|
||||
*/
|
||||
int moduleEntityCameraSetZNear(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraGetZFar(entityId, componentId) -> number
|
||||
*/
|
||||
int moduleEntityCameraGetZFar(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraSetZFar(entityId, componentId, number)
|
||||
*/
|
||||
int moduleEntityCameraSetZFar(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraGetProjType(entityId, componentId) -> number
|
||||
*/
|
||||
int moduleEntityCameraGetProjType(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraSetProjType(entityId, componentId, number)
|
||||
*/
|
||||
int moduleEntityCameraSetProjType(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraGetFov(entityId, componentId) -> number (radians)
|
||||
*/
|
||||
int moduleEntityCameraGetFov(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraSetFov(entityId, componentId, number)
|
||||
*/
|
||||
int moduleEntityCameraSetFov(lua_State *L);
|
||||
|
||||
/**
|
||||
* Lua binding: entityCameraSetOrthographic(entityId, componentId, left, right, top, bottom)
|
||||
*/
|
||||
int moduleEntityCameraSetOrthographic(lua_State *L);
|
||||
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "moduleentitymaterial.h"
|
||||
#include "assert/assert.h"
|
||||
#include "entity/component/display/entitymaterial.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "util/string.h"
|
||||
|
||||
void moduleEntityMaterial(scriptcontext_t *ctx) {
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
lua_State *l = ctx->luaState;
|
||||
|
||||
// shadermaterial_mt: wraps shadermaterial_t* pointer
|
||||
if(luaL_newmetatable(l, "shadermaterial_mt")) {
|
||||
lua_pushcfunction(l, moduleEntityMaterialShaderMaterialIndex);
|
||||
lua_setfield(l, -2, "__index");
|
||||
}
|
||||
lua_pop(l, 1);
|
||||
|
||||
// shaderunlitmaterial_mt: wraps shaderunlitmaterial_t* pointer
|
||||
if(luaL_newmetatable(l, "shaderunlitmaterial_mt")) {
|
||||
lua_pushcfunction(l, moduleEntityMaterialUnlitIndex);
|
||||
lua_setfield(l, -2, "__index");
|
||||
lua_pushcfunction(l, moduleEntityMaterialUnlitNewIndex);
|
||||
lua_setfield(l, -2, "__newindex");
|
||||
}
|
||||
lua_pop(l, 1);
|
||||
|
||||
lua_register(l, "entityMaterialGetShader", moduleEntityMaterialGetShader);
|
||||
lua_register(l, "entityMaterialSetShader", moduleEntityMaterialSetShader);
|
||||
lua_register(l, "entityMaterialGetShaderMaterial", moduleEntityMaterialGetShaderMaterial);
|
||||
}
|
||||
|
||||
int moduleEntityMaterialGetShader(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "Entity ID must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "Component ID must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
shader_t *shader = entityMaterialGetShader(eid, cid);
|
||||
|
||||
if(shader == NULL) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushlightuserdata(L, shader);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityMaterialSetShader(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "Entity ID must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "Component ID must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
|
||||
shader_t *shader = NULL;
|
||||
if(!lua_isnil(L, 3)) {
|
||||
if(!lua_isuserdata(L, 3)) {
|
||||
luaL_error(L, "Shader must be a userdata or nil");
|
||||
return 0;
|
||||
}
|
||||
shader = (shader_t *)lua_touserdata(L, 3);
|
||||
}
|
||||
|
||||
entityMaterialSetShader(eid, cid, shader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int moduleEntityMaterialGetShaderMaterial(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "entityMaterialGetShaderMaterial: entityId must be a number");
|
||||
return 0;
|
||||
}
|
||||
if(!lua_isnumber(L, 2)) {
|
||||
luaL_error(L, "entityMaterialGetShaderMaterial: componentId must be a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
|
||||
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
|
||||
shadermaterial_t *mat = entityMaterialGetShaderMaterial(eid, cid);
|
||||
|
||||
if(mat == NULL) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
shadermaterial_t **pmat = (shadermaterial_t **)lua_newuserdata(
|
||||
L, sizeof(shadermaterial_t *)
|
||||
);
|
||||
*pmat = mat;
|
||||
luaL_getmetatable(L, "shadermaterial_mt");
|
||||
lua_setmetatable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityMaterialShaderMaterialIndex(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
shadermaterial_t *mat = *(shadermaterial_t **)luaL_checkudata(
|
||||
L, 1, "shadermaterial_mt"
|
||||
);
|
||||
assertNotNull(mat, "Shader material pointer cannot be NULL");
|
||||
|
||||
const char_t *key = lua_tostring(L, 2);
|
||||
if(key != NULL && stringCompare(key, "unlit") == 0) {
|
||||
shaderunlitmaterial_t **punlit = (shaderunlitmaterial_t **)lua_newuserdata(
|
||||
L, sizeof(shaderunlitmaterial_t *)
|
||||
);
|
||||
*punlit = &(mat->unlit);
|
||||
luaL_getmetatable(L, "shaderunlitmaterial_mt");
|
||||
lua_setmetatable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityMaterialUnlitIndex(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
shaderunlitmaterial_t *unlit = *(shaderunlitmaterial_t **)luaL_checkudata(
|
||||
L, 1, "shaderunlitmaterial_mt"
|
||||
);
|
||||
assertNotNull(unlit, "Unlit material pointer cannot be NULL");
|
||||
|
||||
const char_t *key = lua_tostring(L, 2);
|
||||
if(key == NULL) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(stringCompare(key, "color") == 0) {
|
||||
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
|
||||
*color = unlit->color;
|
||||
luaL_getmetatable(L, "color_mt");
|
||||
lua_setmetatable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(stringCompare(key, "texture") == 0) {
|
||||
if(unlit->texture == NULL) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushlightuserdata(L, unlit->texture);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleEntityMaterialUnlitNewIndex(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
shaderunlitmaterial_t *unlit = *(shaderunlitmaterial_t **)luaL_checkudata(
|
||||
L, 1, "shaderunlitmaterial_mt"
|
||||
);
|
||||
assertNotNull(unlit, "Unlit material pointer cannot be NULL");
|
||||
|
||||
const char_t *key = lua_tostring(L, 2);
|
||||
if(key == NULL) return 0;
|
||||
|
||||
if(stringCompare(key, "color") == 0) {
|
||||
color_t *color = (color_t *)luaL_checkudata(L, 3, "color_mt");
|
||||
unlit->color = *color;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(stringCompare(key, "texture") == 0) {
|
||||
if(lua_isnil(L, 3)) {
|
||||
unlit->texture = NULL;
|
||||
} else if(lua_isuserdata(L, 3)) {
|
||||
unlit->texture = (texture_t *)lua_touserdata(L, 3);
|
||||
} else {
|
||||
luaL_error(L, "shaderunlitmaterial texture must be a userdata or nil");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user