Compare commits
12 Commits
4cd3355ef1
...
2b9ee8f721
| Author | SHA1 | Date | |
|---|---|---|---|
| 2b9ee8f721 | |||
| d02673e04a | |||
| f0117b8e6e | |||
| bb7c41c754 | |||
| efa583c154 | |||
| d16ea13c14 | |||
| 673d8e0a18 | |||
| 37cfdde1ee | |||
| 0778ffb57a | |||
| 42099f7241 | |||
| c52e1d22b7 | |||
| 0d7b0aadd1 |
+2
-1
@@ -104,4 +104,5 @@ yarn.lock
|
||||
.venv
|
||||
|
||||
/build2
|
||||
/build*
|
||||
/build*
|
||||
/assets/test
|
||||
@@ -14,11 +14,6 @@
|
||||
#error "cameraPushMatrixPlatform must be defined"
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CAMERA_PROJECTION_TYPE_PERSPECTIVE,
|
||||
CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED,
|
||||
CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
|
||||
} cameraprojectiontype_t;
|
||||
|
||||
typedef enum {
|
||||
CAMERA_VIEW_TYPE_MATRIX,
|
||||
+1
-3
@@ -73,6 +73,4 @@ elseif LINUX then
|
||||
|
||||
else
|
||||
print("Unknown platform, no default input bindings set.")
|
||||
end
|
||||
|
||||
sceneSet('scene/main_menu/main_menu.lua')
|
||||
end
|
||||
@@ -1,46 +0,0 @@
|
||||
module('spritebatch')
|
||||
module('camera')
|
||||
module('color')
|
||||
module('ui')
|
||||
module('screen')
|
||||
module('time')
|
||||
module('text')
|
||||
module('tileset')
|
||||
module('texture')
|
||||
module('input')
|
||||
module('shader')
|
||||
module('locale')
|
||||
|
||||
screenSetBackground(colorCornflowerBlue())
|
||||
camera = cameraCreate(CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC)
|
||||
|
||||
text = localeGetText('cart.item_count', 52, 2)
|
||||
|
||||
function sceneDispose()
|
||||
end
|
||||
|
||||
function sceneUpdate()
|
||||
end
|
||||
|
||||
function sceneRender()
|
||||
-- Update camera
|
||||
camera.top = screenGetHeight()
|
||||
camera.right = screenGetWidth()
|
||||
|
||||
shaderBind(SHADER_UNLIT)
|
||||
proj = cameraGetProjectionMatrix(camera)
|
||||
shaderSetMatrix(SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)
|
||||
view = cameraGetViewMatrix(camera)
|
||||
shaderSetMatrix(SHADER_UNLIT, SHADER_UNLIT_VIEW, view)
|
||||
|
||||
-- shaderSetTexture(SHADER_UNLIT, SHADER_UNLIT_TEXTURE, nil)
|
||||
-- spriteBatchPush(
|
||||
-- 32, 32,
|
||||
-- 64, 64,
|
||||
-- colorWhite()
|
||||
-- )
|
||||
-- spriteBatchFlush()
|
||||
|
||||
textDraw(10, 10, text, colorWhite())
|
||||
spriteBatchFlush()
|
||||
end
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "Dominic",
|
||||
"age": 31,
|
||||
"likes": [
|
||||
"thing1",
|
||||
"thing2"
|
||||
],
|
||||
"options": {
|
||||
"someParam": true,
|
||||
"someSubObject": {
|
||||
"someSubSubParam": 52
|
||||
},
|
||||
"someArray": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
if(NOT TARGET fast_obj)
|
||||
FetchContent_Declare(
|
||||
fast_obj
|
||||
GIT_REPOSITORY https://github.com/thisistherk/fast_obj.git
|
||||
GIT_TAG master
|
||||
)
|
||||
FetchContent_MakeAvailable(fast_obj)
|
||||
endif()
|
||||
|
||||
set(fast_obj_FOUND TRUE)
|
||||
set(FAST_OBJ_INCLUDE_DIRS "${fast_obj_SOURCE_DIR}")
|
||||
set(FAST_OBJ_LIBRARIES fast_obj)
|
||||
mark_as_advanced(FAST_OBJ_INCLUDE_DIRS FAST_OBJ_LIBRARIES fast_obj_FOUND)
|
||||
+1
-2
@@ -4,8 +4,7 @@
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
add_subdirectory(dusk)
|
||||
# add_subdirectory(duskrpg)
|
||||
add_subdirectory(dusktrivia)
|
||||
add_subdirectory(duskrpg)
|
||||
|
||||
if(DUSK_TARGET_SYSTEM STREQUAL "linux" OR DUSK_TARGET_SYSTEM STREQUAL "knulli")
|
||||
add_subdirectory(dusklinux)
|
||||
|
||||
@@ -32,6 +32,15 @@ if(NOT yyjson_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT fast_obj_FOUND)
|
||||
find_package(fast_obj REQUIRED)
|
||||
if(fast_obj_FOUND)
|
||||
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC fast_obj)
|
||||
else()
|
||||
message(FATAL_ERROR "fast_obj not found. Please ensure fast_obj is correctly fetched.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT Lua_FOUND)
|
||||
find_package(Lua REQUIRED)
|
||||
if(Lua_FOUND AND NOT TARGET Lua::Lua)
|
||||
@@ -67,6 +76,7 @@ add_subdirectory(asset)
|
||||
add_subdirectory(log)
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(engine)
|
||||
add_subdirectory(entity)
|
||||
add_subdirectory(error)
|
||||
add_subdirectory(event)
|
||||
add_subdirectory(input)
|
||||
|
||||
@@ -10,7 +10,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirectories
|
||||
add_subdirectory(camera)
|
||||
add_subdirectory(framebuffer)
|
||||
add_subdirectory(mesh)
|
||||
add_subdirectory(screen)
|
||||
|
||||
+30
-31
@@ -10,6 +10,7 @@
|
||||
#include "scene/scene.h"
|
||||
#include "display/spritebatch/spritebatch.h"
|
||||
#include "display/mesh/quad.h"
|
||||
#include "display/mesh/cube.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "ui/ui.h"
|
||||
#include "display/text/text.h"
|
||||
@@ -23,24 +24,6 @@
|
||||
#include "script/module/display/moduleshader.h"
|
||||
|
||||
display_t DISPLAY = { 0 };
|
||||
mesh_t mesh;
|
||||
meshvertex_t vertices[3] = {
|
||||
{
|
||||
.color = { 255, 0, 0, 255 },
|
||||
.uv = { 0.0f, 0.0f },
|
||||
.pos = { 0.0f, 0.5f, 0.0f }
|
||||
},
|
||||
{
|
||||
.color = { 0, 255, 0, 255 },
|
||||
.uv = { 0.5f, 1.0f },
|
||||
.pos = { -0.5f, -0.5f, 0.0f }
|
||||
},
|
||||
{
|
||||
.color = { 0, 0, 255, 255 },
|
||||
.uv = { 1.0f, 0.0f },
|
||||
.pos = { 0.5f, -0.5f, 0.0f }
|
||||
}
|
||||
};
|
||||
|
||||
errorret_t displayInit(void) {
|
||||
memoryZero(&DISPLAY, sizeof(DISPLAY));
|
||||
@@ -48,27 +31,43 @@ errorret_t displayInit(void) {
|
||||
#ifdef displayPlatformInit
|
||||
errorChain(displayPlatformInit());
|
||||
#endif
|
||||
errorChain(textureInit(
|
||||
&TEXTURE_WHITE, 4, 4,
|
||||
TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_WHITE_PIXELS }
|
||||
));
|
||||
errorChain(quadInit());
|
||||
errorChain(cubeInit());
|
||||
errorChain(frameBufferInitBackBuffer());
|
||||
errorChain(spriteBatchInit());
|
||||
errorChain(textInit());
|
||||
errorChain(screenInit());
|
||||
|
||||
// Setup initial shader with default values
|
||||
errorChain(shaderInit(&SHADER_UNLIT, &SHADER_UNLIT_DEFINITION));
|
||||
camera_t cam;
|
||||
cameraInit(&cam);
|
||||
mat4 mat;
|
||||
cameraGetProjectionMatrix(&cam, mat);
|
||||
errorChain(shaderBind(&SHADER_UNLIT));
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, mat));
|
||||
cameraGetViewMatrix(&cam, mat);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, mat));
|
||||
glm_mat4_identity(mat);
|
||||
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, mat));
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, NULL));
|
||||
mat4 view, proj, model;
|
||||
glm_lookat(
|
||||
(vec3){ 0.0f, 0.0f, 1.0f },
|
||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
||||
view
|
||||
);
|
||||
|
||||
errorChain(meshInit(&mesh, MESH_PRIMITIVE_TYPE_TRIANGLES, 3, vertices));
|
||||
glm_perspective(
|
||||
glm_rad(45.0f),
|
||||
(float_t)SCREEN.width / (float_t)SCREEN.height,
|
||||
0.1f,
|
||||
100.0f,
|
||||
proj
|
||||
);
|
||||
|
||||
glm_mat4_identity(model);
|
||||
|
||||
errorChain(shaderInit(&SHADER_UNLIT, &SHADER_UNLIT_DEFINITION));
|
||||
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));
|
||||
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, NULL));
|
||||
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#pragma once
|
||||
#include "display/displayplatform.h"
|
||||
#include "display/camera/camera.h"
|
||||
|
||||
// Expecting some definitions to be provided
|
||||
#ifndef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
|
||||
@@ -8,4 +8,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
mesh.c
|
||||
quad.c
|
||||
cube.c
|
||||
)
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "cube.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
mesh_t CUBE_MESH_SIMPLE;
|
||||
meshvertex_t CUBE_MESH_SIMPLE_VERTICES[CUBE_VERTEX_COUNT];
|
||||
|
||||
errorret_t cubeInit() {
|
||||
vec3 min = { 0.0f, 0.0f, 0.0f };
|
||||
vec3 max = { 1.0f, 1.0f, 1.0f };
|
||||
cubeBuffer(CUBE_MESH_SIMPLE_VERTICES, min, max, COLOR_WHITE_4B);
|
||||
errorChain(meshInit(
|
||||
&CUBE_MESH_SIMPLE,
|
||||
CUBE_PRIMITIVE_TYPE,
|
||||
CUBE_VERTEX_COUNT,
|
||||
CUBE_MESH_SIMPLE_VERTICES
|
||||
));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Helper macro: set one vertex position, UV and color.
|
||||
#define CUBE_VERT(i, px, py, pz, u, v) \
|
||||
vertices[i].pos[0] = (px); \
|
||||
vertices[i].pos[1] = (py); \
|
||||
vertices[i].pos[2] = (pz); \
|
||||
vertices[i].uv[0] = (u); \
|
||||
vertices[i].uv[1] = (v); \
|
||||
vertices[i].color = color
|
||||
|
||||
void cubeBuffer(
|
||||
meshvertex_t *vertices,
|
||||
const vec3 min,
|
||||
const vec3 max,
|
||||
const color_t color
|
||||
) {
|
||||
assertNotNull(vertices, "Vertices cannot be NULL");
|
||||
assertNotNull(min, "Min vector cannot be NULL");
|
||||
assertNotNull(max, "Max vector cannot be NULL");
|
||||
|
||||
const float_t x0 = min[0], y0 = min[1], z0 = min[2];
|
||||
const float_t x1 = max[0], y1 = max[1], z1 = max[2];
|
||||
|
||||
// Front face (+Z normal) — CCW when viewed from +Z
|
||||
CUBE_VERT( 0, x0, y0, z1, 0.0f, 0.0f);
|
||||
CUBE_VERT( 1, x1, y0, z1, 1.0f, 0.0f);
|
||||
CUBE_VERT( 2, x1, y1, z1, 1.0f, 1.0f);
|
||||
CUBE_VERT( 3, x0, y0, z1, 0.0f, 0.0f);
|
||||
CUBE_VERT( 4, x1, y1, z1, 1.0f, 1.0f);
|
||||
CUBE_VERT( 5, x0, y1, z1, 0.0f, 1.0f);
|
||||
|
||||
// Back face (-Z normal) — CCW when viewed from -Z
|
||||
CUBE_VERT( 6, x1, y0, z0, 0.0f, 0.0f);
|
||||
CUBE_VERT( 7, x0, y0, z0, 1.0f, 0.0f);
|
||||
CUBE_VERT( 8, x0, y1, z0, 1.0f, 1.0f);
|
||||
CUBE_VERT( 9, x1, y0, z0, 0.0f, 0.0f);
|
||||
CUBE_VERT(10, x0, y1, z0, 1.0f, 1.0f);
|
||||
CUBE_VERT(11, x1, y1, z0, 0.0f, 1.0f);
|
||||
|
||||
// Right face (+X normal) — CCW when viewed from +X
|
||||
CUBE_VERT(12, x1, y0, z1, 0.0f, 0.0f);
|
||||
CUBE_VERT(13, x1, y0, z0, 1.0f, 0.0f);
|
||||
CUBE_VERT(14, x1, y1, z0, 1.0f, 1.0f);
|
||||
CUBE_VERT(15, x1, y0, z1, 0.0f, 0.0f);
|
||||
CUBE_VERT(16, x1, y1, z0, 1.0f, 1.0f);
|
||||
CUBE_VERT(17, x1, y1, z1, 0.0f, 1.0f);
|
||||
|
||||
// Left face (-X normal) — CCW when viewed from -X
|
||||
CUBE_VERT(18, x0, y0, z0, 0.0f, 0.0f);
|
||||
CUBE_VERT(19, x0, y0, z1, 1.0f, 0.0f);
|
||||
CUBE_VERT(20, x0, y1, z1, 1.0f, 1.0f);
|
||||
CUBE_VERT(21, x0, y0, z0, 0.0f, 0.0f);
|
||||
CUBE_VERT(22, x0, y1, z1, 1.0f, 1.0f);
|
||||
CUBE_VERT(23, x0, y1, z0, 0.0f, 1.0f);
|
||||
|
||||
// Top face (+Y normal) — CCW when viewed from +Y
|
||||
CUBE_VERT(24, x0, y1, z1, 0.0f, 0.0f);
|
||||
CUBE_VERT(25, x1, y1, z1, 1.0f, 0.0f);
|
||||
CUBE_VERT(26, x1, y1, z0, 1.0f, 1.0f);
|
||||
CUBE_VERT(27, x0, y1, z1, 0.0f, 0.0f);
|
||||
CUBE_VERT(28, x1, y1, z0, 1.0f, 1.0f);
|
||||
CUBE_VERT(29, x0, y1, z0, 0.0f, 1.0f);
|
||||
|
||||
// Bottom face (-Y normal) — CCW when viewed from -Y
|
||||
CUBE_VERT(30, x0, y0, z0, 0.0f, 0.0f);
|
||||
CUBE_VERT(31, x1, y0, z0, 1.0f, 0.0f);
|
||||
CUBE_VERT(32, x1, y0, z1, 1.0f, 1.0f);
|
||||
CUBE_VERT(33, x0, y0, z0, 0.0f, 0.0f);
|
||||
CUBE_VERT(34, x1, y0, z1, 1.0f, 1.0f);
|
||||
CUBE_VERT(35, x0, y0, z1, 0.0f, 1.0f);
|
||||
|
||||
#undef CUBE_VERT
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/mesh/mesh.h"
|
||||
#include "display/color.h"
|
||||
|
||||
#define CUBE_FACE_COUNT 6
|
||||
#define CUBE_VERTICES_PER_FACE 6
|
||||
#define CUBE_VERTEX_COUNT (CUBE_FACE_COUNT * CUBE_VERTICES_PER_FACE)
|
||||
#define CUBE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
|
||||
|
||||
extern mesh_t CUBE_MESH_SIMPLE;
|
||||
extern meshvertex_t CUBE_MESH_SIMPLE_VERTICES[CUBE_VERTEX_COUNT];
|
||||
|
||||
/**
|
||||
* Initializes the simple unit cube mesh (0,0,0) to (1,1,1).
|
||||
*
|
||||
* @return Error for initialization of the cube mesh.
|
||||
*/
|
||||
errorret_t cubeInit();
|
||||
|
||||
/**
|
||||
* Buffers a 3D axis-aligned cube into the provided vertex array.
|
||||
* Writes CUBE_VERTEX_COUNT vertices (6 faces x 6 vertices, CCW winding).
|
||||
*
|
||||
* @param vertices The vertex array to buffer into (must hold CUBE_VERTEX_COUNT).
|
||||
* @param min The minimum XYZ corner of the cube.
|
||||
* @param max The maximum XYZ corner of the cube.
|
||||
* @param color The color applied to all vertices.
|
||||
*/
|
||||
void cubeBuffer(
|
||||
meshvertex_t *vertices,
|
||||
const vec3 min,
|
||||
const vec3 max,
|
||||
const color_t color
|
||||
);
|
||||
@@ -22,12 +22,6 @@ errorret_t screenInit() {
|
||||
SCREEN.mode = SCREEN_MODE_FIXED_VIEWPORT_HEIGHT;
|
||||
SCREEN.fixedHeight.height = DUSK_DISPLAY_SCREEN_HEIGHT;
|
||||
|
||||
cameraInitOrthographic(&SCREEN.framebufferCamera);
|
||||
SCREEN.framebufferCamera.viewType = CAMERA_VIEW_TYPE_2D;
|
||||
SCREEN.framebufferCamera._2d.position[0] = 0;
|
||||
SCREEN.framebufferCamera._2d.position[1] = 0;
|
||||
SCREEN.framebufferCamera._2d.zoom = 1.0f;
|
||||
|
||||
quadBuffer(
|
||||
SCREEN.frameBufferMeshVertices,
|
||||
0.0f, 0.0f,
|
||||
@@ -352,13 +346,17 @@ errorret_t screenRender() {
|
||||
fbY = (bbHeight - fbHeight) * 0.5f;
|
||||
}
|
||||
|
||||
// Determine back buffer matricies
|
||||
float_t centerX = bbWidth * 0.5f;
|
||||
float_t centerY = bbHeight * 0.5f;
|
||||
|
||||
SCREEN.framebufferCamera.orthographic.left = 0.0f;
|
||||
SCREEN.framebufferCamera.orthographic.right = bbWidth;
|
||||
SCREEN.framebufferCamera.orthographic.top = 0.0f;
|
||||
SCREEN.framebufferCamera.orthographic.bottom = bbHeight;
|
||||
mat4 view, proj, model;
|
||||
glm_ortho(
|
||||
0.0f, bbWidth, bbHeight, 0.0f, 0.01f, 1.0f,
|
||||
proj
|
||||
);
|
||||
glm_mat4_identity(view);
|
||||
glm_mat4_identity(model);
|
||||
|
||||
quadBuffer(
|
||||
SCREEN.frameBufferMeshVertices,
|
||||
centerX - fbWidth * 0.5f, centerY + fbHeight * 0.5f, // top-left
|
||||
@@ -374,10 +372,6 @@ errorret_t screenRender() {
|
||||
);
|
||||
|
||||
shaderBind(&SHADER_UNLIT);
|
||||
mat4 proj, view, model;
|
||||
cameraGetProjectionMatrix(&SCREEN.framebufferCamera, proj);
|
||||
cameraGetViewMatrix(&SCREEN.framebufferCamera, view);
|
||||
glm_mat4_identity(model);
|
||||
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj);
|
||||
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view);
|
||||
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "display/framebuffer/framebuffer.h"
|
||||
#include "display/camera/camera.h"
|
||||
#include "display/mesh/quad.h"
|
||||
#include "display/color.h"
|
||||
|
||||
@@ -50,7 +49,7 @@ typedef struct {
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
framebuffer_t framebuffer;
|
||||
bool_t framebufferReady;
|
||||
camera_t framebufferCamera;
|
||||
// camera_t framebufferCamera;
|
||||
mesh_t frameBufferMesh;
|
||||
meshvertex_t frameBufferMeshVertices[QUAD_VERTEX_COUNT];
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "shader.h"
|
||||
#include "shadermaterial.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
shader_t *bound = NULL;
|
||||
@@ -49,16 +50,28 @@ errorret_t shaderSetTexture(
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// errorret_t shaderSetColor(
|
||||
// shader_t *shader,
|
||||
// const char_t *name,
|
||||
// color_t color
|
||||
// ) {
|
||||
// assertNotNull(shader, "Shader cannot be null");
|
||||
// assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
||||
// errorChain(shaderSetColorPlatform(shader, name, color));
|
||||
// errorOk();
|
||||
// }
|
||||
errorret_t shaderSetColor(
|
||||
shader_t *shader,
|
||||
const char_t *name,
|
||||
color_t color
|
||||
) {
|
||||
assertNotNull(shader, "Shader cannot be null");
|
||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
||||
errorChain(shaderSetColorPlatform(shader, name, color));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t shaderSetMaterial(
|
||||
shader_t *shader,
|
||||
const shadermaterial_t *material
|
||||
) {
|
||||
assertNotNull(shader, "Shader cannot be null");
|
||||
assertNotNull(material, "Material cannot be null");
|
||||
assertTrue(bound == shader, "Shader must be bound.");
|
||||
assertNotNull(shader->definition, "Shader definition cannot be null");
|
||||
assertNotNull(shader->definition->setMaterial, "Def lacks setMaterial");
|
||||
return shader->definition->setMaterial(shader, material);
|
||||
}
|
||||
|
||||
errorret_t shaderDispose(shader_t *shader) {
|
||||
assertNotNull(shader, "Shader cannot be null");
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#error "shaderDisposePlatform must be defined to use shader.h"
|
||||
#endif
|
||||
|
||||
typedef union shadermaterial_u shadermaterial_t;
|
||||
typedef shaderplatform_t shader_t;
|
||||
typedef shaderdefinitionplatform_t shaderdefinition_t;
|
||||
|
||||
@@ -79,15 +80,28 @@ errorret_t shaderSetTexture(
|
||||
* @param color Color to set
|
||||
* @return Error if failure, otherwise errorOk
|
||||
*/
|
||||
// errorret_t shaderSetColor(
|
||||
// shader_t *shader,
|
||||
// const char_t *name,
|
||||
// color_t color
|
||||
// );
|
||||
errorret_t shaderSetColor(
|
||||
shader_t *shader,
|
||||
const char_t *name,
|
||||
color_t color
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets a material's properties in the shader. This is platform dependant.
|
||||
* the definition's upload function pointer.
|
||||
*
|
||||
* @param shader The shader to upload material properties to.
|
||||
* @param material The material data to upload.
|
||||
* @return Error if failure, otherwise errorOk.
|
||||
*/
|
||||
errorret_t shaderSetMaterial(
|
||||
shader_t *shader,
|
||||
const shadermaterial_t *material
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes of a shader. This is platform dependant.
|
||||
*
|
||||
*
|
||||
* @param shader Shader to dispose
|
||||
* @return Error if failure, otherwise errorOk
|
||||
*/
|
||||
|
||||
+5
-4
@@ -1,12 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "cameragl.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
#define cameraPushMatrixPlatform cameraPushMatrixGL
|
||||
#define cameraPopMatrixPlatform cameraPopMatrixGL
|
||||
typedef union shadermaterial_u {
|
||||
shaderunlitmaterial_t unlit;
|
||||
} shadermaterial_t;
|
||||
@@ -6,5 +6,27 @@
|
||||
*/
|
||||
|
||||
#include "shaderunlit.h"
|
||||
#include "display/shader/shadermaterial.h"
|
||||
|
||||
shader_t SHADER_UNLIT = { 0 };
|
||||
shader_t SHADER_UNLIT = {
|
||||
.definition = &SHADER_UNLIT_DEFINITION
|
||||
};
|
||||
|
||||
errorret_t shaderUnlitSetMaterial(
|
||||
shader_t *shader,
|
||||
const shadermaterial_t *material
|
||||
) {
|
||||
errorChain(shaderSetTexture(
|
||||
shader,
|
||||
SHADER_UNLIT_TEXTURE,
|
||||
material->unlit.texture
|
||||
));
|
||||
|
||||
errorChain(shaderSetColor(
|
||||
shader,
|
||||
SHADER_UNLIT_COLOR,
|
||||
material->unlit.color
|
||||
));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -12,7 +12,24 @@
|
||||
#define SHADER_UNLIT_VIEW "u_View"
|
||||
#define SHADER_UNLIT_MODEL "u_Model"
|
||||
#define SHADER_UNLIT_TEXTURE "u_Texture"
|
||||
// #define SHADER_UNLIT_COLOR "u_Color"
|
||||
#define SHADER_UNLIT_COLOR "u_Color"
|
||||
|
||||
typedef struct {
|
||||
color_t color;
|
||||
texture_t *texture;
|
||||
} shaderunlitmaterial_t;
|
||||
|
||||
extern shaderdefinition_t SHADER_UNLIT_DEFINITION;
|
||||
extern shader_t SHADER_UNLIT;
|
||||
extern shader_t SHADER_UNLIT;
|
||||
|
||||
/**
|
||||
* Uploads the unlit material properties to the shader.
|
||||
*
|
||||
* @param shader The shader to upload to.
|
||||
* @param material The material data to upload.
|
||||
* @return Error if failure, otherwise errorOk.
|
||||
*/
|
||||
errorret_t shaderUnlitSetMaterial(
|
||||
shader_t *shader,
|
||||
const shadermaterial_t *material
|
||||
);
|
||||
@@ -11,6 +11,14 @@
|
||||
#include "util/math.h"
|
||||
#include "display/display.h"
|
||||
|
||||
texture_t TEXTURE_WHITE;
|
||||
color_t TEXTURE_WHITE_PIXELS[4*4] = {
|
||||
COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE,
|
||||
COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE,
|
||||
COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE,
|
||||
COLOR_WHITE, COLOR_WHITE, COLOR_WHITE, COLOR_WHITE,
|
||||
};
|
||||
|
||||
errorret_t textureInit(
|
||||
texture_t *texture,
|
||||
const int32_t width,
|
||||
|
||||
@@ -28,6 +28,9 @@ typedef union texturedata_u {
|
||||
color_t *rgbaColors;
|
||||
} texturedata_t;
|
||||
|
||||
extern texture_t TEXTURE_WHITE;
|
||||
extern color_t TEXTURE_WHITE_PIXELS[4*4];
|
||||
|
||||
/**
|
||||
* Initializes a texture.
|
||||
*
|
||||
|
||||
@@ -16,11 +16,22 @@
|
||||
#include "ui/ui.h"
|
||||
#include "script/scriptmanager.h"
|
||||
#include "assert/assert.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "game/game.h"
|
||||
|
||||
#include "asset/loader/json/assetjsonloader.h"
|
||||
#include "display/mesh/quad.h"
|
||||
|
||||
engine_t ENGINE;
|
||||
// texture_t TEXTURE;
|
||||
// color_t TEXTURE_COLORS[] = {
|
||||
// COLOR_RED, COLOR_GREEN, COLOR_MAGENTA, COLOR_CYAN,
|
||||
// COLOR_BLUE, COLOR_WHITE, COLOR_YELLOW, COLOR_BLACK,
|
||||
// COLOR_CYAN, COLOR_MAGENTA, COLOR_GREEN, COLOR_RED,
|
||||
// COLOR_WHITE, COLOR_BLUE, COLOR_BLACK, COLOR_YELLOW
|
||||
// };
|
||||
entityid_t ent1;
|
||||
componentid_t ent1Pos;
|
||||
componentid_t ent1Mesh;
|
||||
componentid_t ent1Mat;
|
||||
|
||||
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
memoryZero(&ENGINE, sizeof(engine_t));
|
||||
@@ -37,9 +48,40 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
errorChain(displayInit());
|
||||
errorChain(uiInit());
|
||||
errorChain(sceneInit());
|
||||
entityManagerInit();
|
||||
errorChain(gameInit());
|
||||
|
||||
// Run the initial script.
|
||||
// FOF
|
||||
entityid_t cam = entityManagerAdd();
|
||||
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
|
||||
entityPositionLookAt(
|
||||
cam,
|
||||
camPos,
|
||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
||||
(vec3){ 3.0f, 3.0f, 3.0f }
|
||||
);
|
||||
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
|
||||
entityCameraSetZFar(cam, camCam, 100.0f);
|
||||
|
||||
ent1 = entityManagerAdd();
|
||||
ent1Pos = entityAddComponent(ent1, COMPONENT_TYPE_POSITION);
|
||||
ent1Mesh = entityAddComponent(ent1, COMPONENT_TYPE_MESH);
|
||||
ent1Mat = entityAddComponent(ent1, COMPONENT_TYPE_MATERIAL);
|
||||
|
||||
// textureInit(&TEXTURE, 4, 4, TEXTURE_FORMAT_RGBA, (texturedata_t){
|
||||
// .rgbaColors = TEXTURE_COLORS
|
||||
// });
|
||||
|
||||
entityMeshSetMesh(ent1, ent1Mesh, &QUAD_MESH_SIMPLE);
|
||||
|
||||
shadermaterial_t *mat = entityMaterialGetShaderMaterial(ent1, ent1Mat);
|
||||
mat->unlit.color = COLOR_MAGENTA;
|
||||
// mat->unlit.texture = &TEXTURE;
|
||||
|
||||
// EOF
|
||||
|
||||
// Run the init script.
|
||||
scriptcontext_t ctx;
|
||||
errorChain(scriptContextInit(&ctx));
|
||||
errorChain(scriptContextExecFile(&ctx, "init.lua"));
|
||||
@@ -52,6 +94,15 @@ errorret_t engineUpdate(void) {
|
||||
timeUpdate();
|
||||
inputUpdate();
|
||||
|
||||
vec3 rotation;
|
||||
entityPositionGetRotation(ent1, ent1Pos, rotation);
|
||||
#if DUSK_TIME_DYNAMIC
|
||||
rotation[1] += 1.0f * TIME.dynamicDelta;
|
||||
#else
|
||||
rotation[1] += 1.0f * TIME.delta;
|
||||
#endif
|
||||
entityPositionSetRotation(ent1, ent1Pos, rotation);
|
||||
|
||||
uiUpdate();
|
||||
errorChain(sceneUpdate());
|
||||
errorChain(gameUpdate());
|
||||
@@ -69,6 +120,7 @@ void engineExit(void) {
|
||||
errorret_t engineDispose(void) {
|
||||
sceneDispose();
|
||||
errorChain(gameDispose());
|
||||
entityManagerDispose();
|
||||
localeManagerDispose();
|
||||
uiDispose();
|
||||
errorChain(displayDispose());
|
||||
|
||||
@@ -6,5 +6,10 @@
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
modulemap.c
|
||||
)
|
||||
entity.c
|
||||
entitymanager.c
|
||||
component.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(component)
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entitymanager.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
componentdefinition_t COMPONENT_DEFINITIONS[] = {
|
||||
[COMPONENT_TYPE_NULL] = { 0 },
|
||||
|
||||
#define X(enumName, type, field, iMethod, dMethod) \
|
||||
[COMPONENT_TYPE_##enumName] = { .init = iMethod, .dispose = dMethod },
|
||||
#include "componentlist.h"
|
||||
#undef X
|
||||
|
||||
[COMPONENT_TYPE_COUNT] = { 0 }
|
||||
};
|
||||
|
||||
void componentInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const componenttype_t type
|
||||
) {
|
||||
assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB");
|
||||
assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB");
|
||||
assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB");
|
||||
assertTrue(type != COMPONENT_TYPE_NULL, "Cannot initialize null component");
|
||||
|
||||
componentindex_t index = componentGetIndex(entityId, componentId);
|
||||
component_t *cmp = &ENTITY_MANAGER.components[index];
|
||||
memoryZero(cmp, sizeof(component_t));
|
||||
|
||||
cmp->type = type;
|
||||
if(COMPONENT_DEFINITIONS[type].init) {
|
||||
COMPONENT_DEFINITIONS[type].init(entityId, componentId);
|
||||
}
|
||||
}
|
||||
|
||||
void * componentGetData(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const componenttype_t type
|
||||
) {
|
||||
assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB");
|
||||
assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB");
|
||||
assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB");
|
||||
assertTrue(type != COMPONENT_TYPE_NULL, "Cannot get data of null component");
|
||||
|
||||
componentindex_t index = componentGetIndex(entityId, componentId);
|
||||
component_t *cmp = &ENTITY_MANAGER.components[index];
|
||||
assertTrue(cmp->type == type, "Component type mismatch");
|
||||
|
||||
return &cmp->data;
|
||||
}
|
||||
|
||||
componentindex_t componentGetIndex(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB");
|
||||
assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB");
|
||||
return (entityId * ENTITY_COMPONENT_COUNT_MAX) + componentId;
|
||||
}
|
||||
|
||||
entityid_t componentGetEntitiesWithComponent(
|
||||
const componenttype_t type,
|
||||
entityid_t outEntities[ENTITY_COUNT_MAX],
|
||||
componentid_t outComponents[ENTITY_COUNT_MAX]
|
||||
) {
|
||||
assertTrue(type < COMPONENT_TYPE_COUNT, "Component type OOB");
|
||||
assertTrue(type != COMPONENT_TYPE_NULL, "Cannot check NULL type");
|
||||
assertNotNull(outEntities, "Output entities array cannot be null");
|
||||
assertNotNull(outComponents, "Output components array cannot be null");
|
||||
|
||||
entityid_t written = 0;
|
||||
for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) {
|
||||
componentid_t used = ENTITY_MANAGER.entitiesWithComponent[
|
||||
type * ENTITY_COUNT_MAX + i
|
||||
];
|
||||
if(used == 0xFF) continue;
|
||||
assertTrue(
|
||||
ENTITY_MANAGER.components[componentGetIndex(i, used)].type == type,
|
||||
"Component type mismatch in entitiesWithComponent lookup"
|
||||
);
|
||||
assertTrue(
|
||||
(ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) != 0,
|
||||
"Inactive entity in entitiesWithComponent lookup"
|
||||
);
|
||||
assertTrue(
|
||||
used < ENTITY_COMPONENT_COUNT_MAX,
|
||||
"Component ID OOB in entitiesWithComponent lookup"
|
||||
);
|
||||
assertTrue(
|
||||
componentGetIndex(i, used) < ENTITY_COUNT_MAX * ENTITY_COMPONENT_COUNT_MAX,
|
||||
"Component index OOB in entitiesWithComponent lookup"
|
||||
);
|
||||
assertTrue(
|
||||
ENTITY_MANAGER.components[componentGetIndex(i,used)].type == type,
|
||||
"Component type mismatch in entitiesWithComponent lookup"
|
||||
);
|
||||
outComponents[written] = used;
|
||||
outEntities[written++] = i;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
void componentDispose(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
assertTrue(entityId < ENTITY_COUNT_MAX, "Entity ID OOB");
|
||||
assertTrue(componentId < ENTITY_COMPONENT_COUNT_MAX, "Component ID OOB");
|
||||
|
||||
componentindex_t index = componentGetIndex(entityId, componentId);
|
||||
component_t *cmp = &ENTITY_MANAGER.components[index];
|
||||
if(cmp->type == COMPONENT_TYPE_NULL) return;
|
||||
|
||||
if(COMPONENT_DEFINITIONS[cmp->type].dispose) {
|
||||
COMPONENT_DEFINITIONS[cmp->type].dispose(entityId, componentId);
|
||||
}
|
||||
|
||||
cmp->type = COMPONENT_TYPE_NULL;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entitybase.h"
|
||||
|
||||
#define X(enumName, type, field, init, dispose) \
|
||||
// do nothing
|
||||
#include "componentlist.h"
|
||||
#undef X
|
||||
|
||||
typedef union {
|
||||
#define X(enumName, type, field, init, dispose) type field;
|
||||
#include "componentlist.h"
|
||||
#undef X
|
||||
} componentdata_t;
|
||||
|
||||
typedef struct {
|
||||
void (*init)(const entityid_t, const componentid_t);
|
||||
void (*dispose)(const entityid_t, const componentid_t);
|
||||
} componentdefinition_t;
|
||||
|
||||
typedef enum {
|
||||
COMPONENT_TYPE_NULL,
|
||||
|
||||
#define X(enumName, type, field, init, dispose) \
|
||||
COMPONENT_TYPE_##enumName,
|
||||
#include "componentlist.h"
|
||||
#undef X
|
||||
|
||||
COMPONENT_TYPE_COUNT
|
||||
} componenttype_t;
|
||||
|
||||
typedef struct {
|
||||
componenttype_t type;
|
||||
componentdata_t data;
|
||||
} component_t;
|
||||
|
||||
extern componentdefinition_t COMPONENT_DEFINITIONS[];
|
||||
|
||||
/**
|
||||
* Initializes a component of the given type for the entity with component ID.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param type The type of the component to initialize.
|
||||
*/
|
||||
void componentInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const componenttype_t type
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the pointer to the data of a component for the entity with component ID.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param type The type of the component to get, only used for assertion.
|
||||
* @return A pointer to the component data.
|
||||
*/
|
||||
void * componentGetData(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
const componenttype_t type
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the index of a component for the entity with component ID.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @return The index of the component in the component array.
|
||||
*/
|
||||
componentindex_t componentGetIndex(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the entity IDs of all entities with a component of the given type.
|
||||
*
|
||||
* @param type The type of the component to get entities for.
|
||||
* @param outEntities An array to write the entity IDs to, must be at least
|
||||
* ENTITY_COUNT_MAX in size.
|
||||
* @param outComponents An array to write the component IDs to.
|
||||
* @return The number of entity IDs written to outEntities.
|
||||
*/
|
||||
entityid_t componentGetEntitiesWithComponent(
|
||||
const componenttype_t type,
|
||||
entityid_t outEntities[ENTITY_COUNT_MAX],
|
||||
componentid_t outComponents[ENTITY_COUNT_MAX]
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes of a component for the entity with component ID.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
*/
|
||||
void componentDispose(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Subdirectories
|
||||
add_subdirectory(display)
|
||||
+4
-2
@@ -4,8 +4,10 @@
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
cameradolphin.c
|
||||
entityposition.c
|
||||
entitycamera.c
|
||||
entitymesh.c
|
||||
entitymaterial.c
|
||||
)
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entity/entitymanager.h"
|
||||
#include "display/framebuffer/framebuffer.h"
|
||||
#include "display/screen/screen.h"
|
||||
|
||||
void entityCameraInit(const entityid_t ent, const componentid_t comp) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
cam->nearClip = 0.1f;
|
||||
cam->farClip = 100.0f;
|
||||
cam->projType = ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE;
|
||||
cam->perspective.fov = glm_rad(45.0f);
|
||||
}
|
||||
|
||||
float_t entityCameraGetZNear(const entityid_t ent, const componentid_t comp) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
return cam->nearClip;
|
||||
}
|
||||
|
||||
void entityCameraSetZNear(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t zNear
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
cam->nearClip = zNear;
|
||||
}
|
||||
|
||||
float_t entityCameraGetZFar(const entityid_t ent, const componentid_t comp) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
return cam->farClip;
|
||||
}
|
||||
|
||||
void entityCameraSetZFar(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t zFar
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
cam->farClip = zFar;
|
||||
}
|
||||
|
||||
void entityCameraGetProjection(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
mat4 out
|
||||
) {
|
||||
entitycamera_t *cam = (entitycamera_t *)componentGetData(
|
||||
ent, comp, COMPONENT_TYPE_CAMERA
|
||||
);
|
||||
|
||||
if(
|
||||
cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
|
||||
cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
||||
) {
|
||||
glm_mat4_identity(out);
|
||||
glm_perspective(
|
||||
cam->perspective.fov,
|
||||
SCREEN.aspect,
|
||||
cam->nearClip,
|
||||
cam->farClip,
|
||||
out
|
||||
);
|
||||
|
||||
if(cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED) {
|
||||
out[1][1] *= -1.0f;
|
||||
}
|
||||
} else if(cam->projType == ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||
glm_mat4_identity(out);
|
||||
glm_ortho(
|
||||
cam->orthographic.left,
|
||||
cam->orthographic.right,
|
||||
cam->orthographic.top,
|
||||
cam->orthographic.bottom,
|
||||
cam->nearClip,
|
||||
cam->farClip,
|
||||
out
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity/entitybase.h"
|
||||
|
||||
typedef enum {
|
||||
ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE,
|
||||
ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED,
|
||||
ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
|
||||
} entitycameraprojectiontype_t;
|
||||
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
float_t fov;
|
||||
} perspective;
|
||||
|
||||
struct {
|
||||
float_t left;
|
||||
float_t right;
|
||||
float_t top;
|
||||
float_t bottom;
|
||||
} orthographic;
|
||||
};
|
||||
|
||||
float_t nearClip;
|
||||
float_t farClip;
|
||||
entitycameraprojectiontype_t projType;
|
||||
} entitycamera_t;
|
||||
|
||||
/**
|
||||
* Initializes an entity camera component.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
*/
|
||||
void entityCameraInit(const entityid_t ent, const componentid_t comp);
|
||||
|
||||
/**
|
||||
* Renders out the projection matrix for the given camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @param out The output projection matrix.
|
||||
*/
|
||||
void entityCameraGetProjection(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
mat4 out
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the near clip distance of a camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @return The near clip distance.
|
||||
*/
|
||||
float_t entityCameraGetZNear(const entityid_t ent, const componentid_t comp);
|
||||
|
||||
/**
|
||||
* Sets the near clip distance of a camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @param zNear The near clip distance.
|
||||
*/
|
||||
void entityCameraSetZNear(
|
||||
const entityid_t ent,
|
||||
const componentid_t comp,
|
||||
const float_t zNear
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the far clip distance of a camera.
|
||||
*
|
||||
* @param ent The entity ID.
|
||||
* @param comp The component ID.
|
||||
* @return The far clip distance.
|
||||
*/
|
||||
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
|
||||
);
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entity/entitymanager.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
void entityMaterialInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entitymaterial_t *mat = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||
);
|
||||
mat->shader = &SHADER_UNLIT;
|
||||
mat->material.unlit.color = COLOR_WHITE;
|
||||
}
|
||||
|
||||
shadermaterial_t * entityMaterialGetShaderMaterial(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entitymaterial_t *mat = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||
);
|
||||
return &mat->material;
|
||||
}
|
||||
|
||||
shader_t * entityMaterialGetShader(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entitymaterial_t *mat = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||
);
|
||||
return mat->shader;
|
||||
}
|
||||
|
||||
void entityMaterialSetShader(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
shader_t *shader
|
||||
) {
|
||||
entitymaterial_t *mat = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MATERIAL
|
||||
);
|
||||
mat->shader = shader;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity/entitybase.h"
|
||||
#include "display/shader/shadermaterial.h"
|
||||
|
||||
typedef struct {
|
||||
shader_t *shader;
|
||||
shadermaterial_t material;
|
||||
} entitymaterial_t;
|
||||
|
||||
/**
|
||||
* Initializes the entity material component, defaulting to the unlit shader.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
*/
|
||||
void entityMaterialInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the shader material for the given entity and component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @return The shader material for the given entity and component.
|
||||
*/
|
||||
shadermaterial_t * entityMaterialGetShaderMaterial(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the shader for the given entity and component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @return The shader for the given entity and component.
|
||||
*/
|
||||
shader_t * entityMaterialGetShader(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the shader for the given entity and component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param shader The shader to set.
|
||||
*/
|
||||
void entityMaterialSetShader(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
shader_t *shader
|
||||
);
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entity/entitymanager.h"
|
||||
|
||||
void entityMeshInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entitymesh_t *comp = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
comp->mesh = NULL;
|
||||
}
|
||||
|
||||
mesh_t * entityMeshGetMesh(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entitymesh_t *comp = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
return comp->mesh;
|
||||
}
|
||||
|
||||
void entityMeshSetMesh(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
mesh_t *mesh
|
||||
) {
|
||||
entitymesh_t *comp = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_MESH
|
||||
);
|
||||
comp->mesh = mesh;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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 "display/mesh/mesh.h"
|
||||
|
||||
typedef struct {
|
||||
mesh_t *mesh;
|
||||
} entitymesh_t;
|
||||
|
||||
/**
|
||||
* Initializes the entity mesh component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
*/
|
||||
void entityMeshInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves the mesh associated with the entity mesh component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @return A pointer to the mesh associated with the entity mesh component.
|
||||
*/
|
||||
mesh_t * entityMeshGetMesh(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the mesh associated with the entity mesh component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param mesh A pointer to the mesh to associate with the entity mesh component.
|
||||
*/
|
||||
void entityMeshSetMesh(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
mesh_t *mesh
|
||||
);
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entity/entitymanager.h"
|
||||
|
||||
void entityPositionInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
|
||||
glm_vec3_zero(pos->position);
|
||||
glm_vec3_zero(pos->rotation);
|
||||
glm_vec3_one(pos->scale);
|
||||
glm_mat4_identity(pos->transform);
|
||||
}
|
||||
|
||||
void entityPositionLookAt(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 target,
|
||||
vec3 up,
|
||||
vec3 eye
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_lookat(eye, target, up, pos->transform);
|
||||
entityPositionDecompose(pos);
|
||||
}
|
||||
|
||||
void entityPositionGetTransform(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
mat4 dest
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_mat4_copy(pos->transform, dest);
|
||||
}
|
||||
|
||||
void entityPositionGetPosition(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 dest
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_vec3_copy(pos->position, dest);
|
||||
}
|
||||
|
||||
void entityPositionSetPosition(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 position
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_vec3_copy(position, pos->position);
|
||||
entityPositionRebuild(pos);
|
||||
}
|
||||
|
||||
void entityPositionGetRotation(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 dest
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_vec3_copy(pos->rotation, dest);
|
||||
}
|
||||
|
||||
void entityPositionSetRotation(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 rotation
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_vec3_copy(rotation, pos->rotation);
|
||||
entityPositionRebuild(pos);
|
||||
}
|
||||
|
||||
void entityPositionGetScale(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 dest
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_vec3_copy(pos->scale, dest);
|
||||
}
|
||||
|
||||
void entityPositionSetScale(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 scale
|
||||
) {
|
||||
entityposition_t *pos = componentGetData(
|
||||
entityId, componentId, COMPONENT_TYPE_POSITION
|
||||
);
|
||||
glm_vec3_copy(scale, pos->scale);
|
||||
entityPositionRebuild(pos);
|
||||
}
|
||||
|
||||
void entityPositionRebuild(entityposition_t *pos) {
|
||||
glm_mat4_identity(pos->transform);
|
||||
glm_translate(pos->transform, pos->position);
|
||||
glm_rotate_x(pos->transform, pos->rotation[0], pos->transform);
|
||||
glm_rotate_y(pos->transform, pos->rotation[1], pos->transform);
|
||||
glm_rotate_z(pos->transform, pos->rotation[2], pos->transform);
|
||||
glm_scale(pos->transform, pos->scale);
|
||||
}
|
||||
|
||||
void entityPositionDecompose(entityposition_t *pos) {
|
||||
// Translation: column 3
|
||||
pos->position[0] = pos->transform[3][0];
|
||||
pos->position[1] = pos->transform[3][1];
|
||||
pos->position[2] = pos->transform[3][2];
|
||||
|
||||
// Scale: length of each basis column (xyz only)
|
||||
pos->scale[0] = sqrtf(
|
||||
pos->transform[0][0] * pos->transform[0][0] +
|
||||
pos->transform[0][1] * pos->transform[0][1] +
|
||||
pos->transform[0][2] * pos->transform[0][2]
|
||||
);
|
||||
pos->scale[1] = sqrtf(
|
||||
pos->transform[1][0] * pos->transform[1][0] +
|
||||
pos->transform[1][1] * pos->transform[1][1] +
|
||||
pos->transform[1][2] * pos->transform[1][2]
|
||||
);
|
||||
pos->scale[2] = sqrtf(
|
||||
pos->transform[2][0] * pos->transform[2][0] +
|
||||
pos->transform[2][1] * pos->transform[2][1] +
|
||||
pos->transform[2][2] * pos->transform[2][2]
|
||||
);
|
||||
|
||||
// Normalize columns to isolate the rotation matrix
|
||||
float invS0 = pos->scale[0] > 0.0f ? 1.0f / pos->scale[0] : 0.0f;
|
||||
float invS1 = pos->scale[1] > 0.0f ? 1.0f / pos->scale[1] : 0.0f;
|
||||
float invS2 = pos->scale[2] > 0.0f ? 1.0f / pos->scale[2] : 0.0f;
|
||||
|
||||
mat4 r;
|
||||
glm_mat4_identity(r);
|
||||
r[0][0] = pos->transform[0][0] * invS0;
|
||||
r[0][1] = pos->transform[0][1] * invS0;
|
||||
r[0][2] = pos->transform[0][2] * invS0;
|
||||
r[1][0] = pos->transform[1][0] * invS1;
|
||||
r[1][1] = pos->transform[1][1] * invS1;
|
||||
r[1][2] = pos->transform[1][2] * invS1;
|
||||
r[2][0] = pos->transform[2][0] * invS2;
|
||||
r[2][1] = pos->transform[2][1] * invS2;
|
||||
r[2][2] = pos->transform[2][2] * invS2;
|
||||
|
||||
// Extract XYZ euler angles (R = Rx * Ry * Rz, column-major)
|
||||
// r[2][0] = sin(Y), r[2][1] = -sin(X)*cos(Y), r[2][2] = cos(X)*cos(Y)
|
||||
// r[0][0] = cos(Y)*cos(Z), r[1][0] = -cos(Y)*sin(Z)
|
||||
float sinBeta = glm_clamp(r[2][0], -1.0f, 1.0f);
|
||||
pos->rotation[1] = asinf(sinBeta);
|
||||
float cosBeta = cosf(pos->rotation[1]);
|
||||
|
||||
if (fabsf(cosBeta) > 1e-6f) {
|
||||
pos->rotation[0] = atan2f(-r[2][1], r[2][2]);
|
||||
pos->rotation[2] = atan2f(-r[1][0], r[0][0]);
|
||||
} else {
|
||||
// Gimbal lock: pin Z to 0, recover X from the remaining degree of freedom
|
||||
pos->rotation[2] = 0.0f;
|
||||
pos->rotation[0] = (sinBeta > 0.0f)
|
||||
? atan2f(r[0][1], r[1][1])
|
||||
: -atan2f(r[0][1], r[1][1]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity/entitybase.h"
|
||||
|
||||
typedef struct {
|
||||
mat4 transform;
|
||||
vec3 position;
|
||||
vec3 rotation;
|
||||
vec3 scale;
|
||||
} entityposition_t;
|
||||
|
||||
/**
|
||||
* Initialize the entity position component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
*/
|
||||
void entityPositionInit(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId
|
||||
);
|
||||
|
||||
/**
|
||||
* Transforms the entity's position to look at a target point.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param target The target point to look at.
|
||||
* @param up The up vector for the look at transformation.
|
||||
* @param eye The position of the camera/eye for the look at transformation.
|
||||
*/
|
||||
void entityPositionLookAt(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 target,
|
||||
vec3 up,
|
||||
vec3 eye
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the transform matrix of the entity position component.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param dest The destination matrix to write the transform to.
|
||||
*/
|
||||
void entityPositionGetTransform(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
mat4 dest
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the cached position of the entity.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param dest The destination vec3 to write the position to.
|
||||
*/
|
||||
void entityPositionGetPosition(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 dest
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the position of the entity and rebuilds the transform matrix.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param position The new position.
|
||||
*/
|
||||
void entityPositionSetPosition(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 position
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the cached euler rotation (XYZ, radians) of the entity.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param dest The destination vec3 to write the rotation to.
|
||||
*/
|
||||
void entityPositionGetRotation(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 dest
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the euler rotation (XYZ, radians) of the entity and rebuilds the
|
||||
* transform matrix.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param rotation The new euler rotation in radians.
|
||||
*/
|
||||
void entityPositionSetRotation(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 rotation
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the cached scale of the entity.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param dest The destination vec3 to write the scale to.
|
||||
*/
|
||||
void entityPositionGetScale(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 dest
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the scale of the entity and rebuilds the transform matrix.
|
||||
*
|
||||
* @param entityId The entity ID.
|
||||
* @param componentId The component ID.
|
||||
* @param scale The new scale.
|
||||
*/
|
||||
void entityPositionSetScale(
|
||||
const entityid_t entityId,
|
||||
const componentid_t componentId,
|
||||
vec3 scale
|
||||
);
|
||||
|
||||
/**
|
||||
* Internal function to rebuild the transform matrix of the entity position
|
||||
* component based on the current position, rotation, and scale.
|
||||
*/
|
||||
void entityPositionRebuild(entityposition_t *pos);
|
||||
|
||||
/**
|
||||
* Decomposes the transform matrix back into the position, rotation (XYZ euler,
|
||||
* radians), and scale cache fields. Call after any direct matrix modification.
|
||||
*
|
||||
* @param pos Pointer to the entity position component data.
|
||||
*/
|
||||
void entityPositionDecompose(entityposition_t *pos);
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entity/component/display/entityposition.h"
|
||||
#include "entity/component/display/entitycamera.h"
|
||||
#include "entity/component/display/entitymesh.h"
|
||||
#include "entity/component/display/entitymaterial.h"
|
||||
|
||||
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
||||
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
||||
X(MESH, entitymesh_t, mesh, entityMeshInit, NULL)
|
||||
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entitymanager.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void entityInit(const entityid_t entityId) {
|
||||
entity_t *ent = &ENTITY_MANAGER.entities[entityId];
|
||||
|
||||
memoryZero(ent, sizeof(entity_t));
|
||||
|
||||
// Mark all component types not using this entity.
|
||||
for(
|
||||
componenttype_t compType = 0;
|
||||
compType < COMPONENT_TYPE_COUNT;
|
||||
compType++
|
||||
) {
|
||||
ENTITY_MANAGER.entitiesWithComponent[
|
||||
compType * ENTITY_COUNT_MAX + entityId
|
||||
] = 0xFF;
|
||||
}
|
||||
|
||||
ent->state |= ENTITY_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
componentid_t entityAddComponent(
|
||||
const entityid_t entityId,
|
||||
const componenttype_t type
|
||||
) {
|
||||
componentindex_t compInd;
|
||||
entity_t *ent = &ENTITY_MANAGER.entities[entityId];
|
||||
|
||||
for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) {
|
||||
compInd = componentGetIndex(entityId, i);
|
||||
if(ENTITY_MANAGER.components[compInd].type != COMPONENT_TYPE_NULL) {
|
||||
assertTrue(
|
||||
ENTITY_MANAGER.components[compInd].type != type,
|
||||
"Entity already has component of this type"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
componentInit(entityId, i, type);
|
||||
ENTITY_MANAGER.entitiesWithComponent[
|
||||
type * ENTITY_COUNT_MAX + entityId
|
||||
] = i;
|
||||
return i;
|
||||
}
|
||||
|
||||
assertUnreachable("Entity has no more component slots available");
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
componentid_t entityGetComponent(
|
||||
const entityid_t entityId,
|
||||
const componenttype_t type
|
||||
) {
|
||||
componentid_t compId = ENTITY_MANAGER.entitiesWithComponent[
|
||||
type * ENTITY_COUNT_MAX + entityId
|
||||
];
|
||||
if(compId == 0xFF) return compId;
|
||||
assertTrue(
|
||||
ENTITY_MANAGER.components[componentGetIndex(entityId, compId)].type == type,
|
||||
"Component type mismatch"
|
||||
);
|
||||
return compId;
|
||||
}
|
||||
|
||||
void entityDispose(const entityid_t entityId) {
|
||||
componentindex_t compInd;
|
||||
entity_t *ent = &ENTITY_MANAGER.entities[entityId];
|
||||
|
||||
for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) {
|
||||
compInd = componentGetIndex(entityId, i);
|
||||
if(ENTITY_MANAGER.components[compInd].type == COMPONENT_TYPE_NULL) continue;
|
||||
componentDispose(entityId, i);
|
||||
ENTITY_MANAGER.entitiesWithComponent[
|
||||
ENTITY_MANAGER.components[compInd].type * ENTITY_COUNT_MAX + entityId
|
||||
] = 0xFF;
|
||||
}
|
||||
|
||||
ent->state = 0;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "component.h"
|
||||
|
||||
#define ENTITY_STATE_ACTIVE (1 << 0)
|
||||
|
||||
typedef struct {
|
||||
uint8_t state;
|
||||
} entity_t;
|
||||
|
||||
/**
|
||||
* Initializes an entity with the given ID.
|
||||
*
|
||||
* @param entityId The ID of the entity to initialize.
|
||||
*/
|
||||
void entityInit(const entityid_t entityId);
|
||||
|
||||
/**
|
||||
* Adds a component of the given type to the entity with the given ID.
|
||||
*
|
||||
* @param entityId The ID of the entity to add the component to.
|
||||
* @param type The type of the component to add.
|
||||
* @return The ID of the entity with component.
|
||||
*/
|
||||
componentid_t entityAddComponent(
|
||||
const entityid_t entityId,
|
||||
const componenttype_t type
|
||||
);
|
||||
|
||||
/**
|
||||
* Gets the ID of the component of the given type on the entity with the given ID.
|
||||
*
|
||||
* @param entityId The ID of the entity to get the component from.
|
||||
* @param type The type of the component to get.
|
||||
* @return The ID of the component.
|
||||
*/
|
||||
componentid_t entityGetComponent(
|
||||
const entityid_t entityId,
|
||||
const componenttype_t type
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes of an entity with the given ID.
|
||||
*
|
||||
* @param entityId The ID of the entity to dispose of.
|
||||
*/
|
||||
void entityDispose(const entityid_t entityId);
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#define ENTITY_COUNT_MAX 128
|
||||
#define ENTITY_COMPONENT_COUNT_MAX 24
|
||||
|
||||
typedef uint8_t entityid_t;
|
||||
typedef uint8_t componentid_t;
|
||||
typedef uint16_t componentindex_t;
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "entitymanager.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
entitymanager_t ENTITY_MANAGER;
|
||||
|
||||
void entityManagerInit(void) {
|
||||
memoryZero(&ENTITY_MANAGER, sizeof(entitymanager_t));
|
||||
memorySet(
|
||||
ENTITY_MANAGER.entitiesWithComponent, 0xFF,
|
||||
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
|
||||
);
|
||||
}
|
||||
|
||||
entityid_t entityManagerAdd() {
|
||||
for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) {
|
||||
if((ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) != 0) continue;
|
||||
entityInit(i);
|
||||
return i;
|
||||
}
|
||||
assertUnreachable("No more entity IDs available");
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
void entityManagerDispose(void) {
|
||||
for(entityid_t i = 0; i < ENTITY_COUNT_MAX; i++) {
|
||||
if((ENTITY_MANAGER.entities[i].state & ENTITY_STATE_ACTIVE) == 0) continue;
|
||||
entityDispose(i);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "entity.h"
|
||||
|
||||
typedef struct {
|
||||
entity_t entities[ENTITY_COUNT_MAX];
|
||||
component_t components[ENTITY_COUNT_MAX * ENTITY_COMPONENT_COUNT_MAX];
|
||||
componentid_t entitiesWithComponent[COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX];
|
||||
} entitymanager_t;
|
||||
|
||||
extern entitymanager_t ENTITY_MANAGER;
|
||||
|
||||
/**
|
||||
* Initializes the entity manager.
|
||||
*/
|
||||
void entityManagerInit(void);
|
||||
|
||||
/**
|
||||
* Adds / Reserves a new entity ID.
|
||||
*
|
||||
* @return The new entity ID.
|
||||
*/
|
||||
entityid_t entityManagerAdd();
|
||||
|
||||
/**
|
||||
* Disposes of the entity manager, in turn freeing all entities and components.
|
||||
*/
|
||||
void entityManagerDispose(void);
|
||||
+80
-52
@@ -8,15 +8,16 @@
|
||||
#include "util/memory.h"
|
||||
#include "log/log.h"
|
||||
#include "time/time.h"
|
||||
#include "display/camera/camera.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "entity/entitymanager.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/mesh/cube.h"
|
||||
|
||||
scene_t SCENE;
|
||||
|
||||
errorret_t sceneInit(void) {
|
||||
memoryZero(&SCENE, sizeof(scene_t));
|
||||
|
||||
errorChain(scriptContextInit(&SCENE.scriptContext));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -26,69 +27,96 @@ errorret_t sceneUpdate(void) {
|
||||
errorOk();
|
||||
}
|
||||
#endif
|
||||
|
||||
lua_getglobal(SCENE.scriptContext.luaState, "sceneUpdate");
|
||||
if(!lua_isfunction(SCENE.scriptContext.luaState, -1)) {
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
if(lua_pcall(SCENE.scriptContext.luaState, 0, 0, 0) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(SCENE.scriptContext.luaState, -1);
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
errorThrow("Failed to call function '%s': %s", "sceneUpdate", strErr);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t sceneRender(void) {
|
||||
lua_getglobal(SCENE.scriptContext.luaState, "sceneRender");
|
||||
if(!lua_isfunction(SCENE.scriptContext.luaState, -1)) {
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
errorOk();
|
||||
// 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(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 each mesh.
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if(lua_pcall(SCENE.scriptContext.luaState, 0, 0, 0) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(SCENE.scriptContext.luaState, -1);
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
errorThrow("Failed to call function '%s': %s", "sceneRender", strErr);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t sceneSet(const char_t *script) {
|
||||
// Cleanup old script context.
|
||||
lua_getglobal(SCENE.scriptContext.luaState, "sceneDispose");
|
||||
if(lua_isfunction(SCENE.scriptContext.luaState, -1)) {
|
||||
if(lua_pcall(SCENE.scriptContext.luaState, 0, 0, 0) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(SCENE.scriptContext.luaState, -1);
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
errorThrow("Failed to call function '%s': %s", "sceneDispose", strErr);
|
||||
}
|
||||
} else {
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
}
|
||||
scriptContextDispose(&SCENE.scriptContext);
|
||||
|
||||
// Create a new script context.
|
||||
errorChain(scriptContextInit(&SCENE.scriptContext));
|
||||
errorChain(scriptContextExecFile(&SCENE.scriptContext, script));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void sceneDispose(void) {
|
||||
lua_getglobal(SCENE.scriptContext.luaState, "sceneDispose");
|
||||
if(lua_isfunction(SCENE.scriptContext.luaState, -1)) {
|
||||
if(lua_pcall(SCENE.scriptContext.luaState, 0, 0, 0) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(SCENE.scriptContext.luaState, -1);
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
logDebug("Failed to call function '%s': %s\n", "sceneDispose", strErr);
|
||||
}
|
||||
} else {
|
||||
lua_pop(SCENE.scriptContext.luaState, 1);
|
||||
}
|
||||
|
||||
scriptContextDispose(&SCENE.scriptContext);
|
||||
|
||||
}
|
||||
@@ -6,10 +6,10 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
#include "error/error.h"
|
||||
|
||||
typedef struct {
|
||||
scriptcontext_t scriptContext;
|
||||
void *nothing;
|
||||
} scene_t;
|
||||
|
||||
extern scene_t SCENE;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
moduleglm.c
|
||||
modulecamera.c
|
||||
modulespritebatch.c
|
||||
moduleglm.c
|
||||
modulecolor.c
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "modulecamera.h"
|
||||
#include "assert/assert.h"
|
||||
#include "display/camera/camera.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
|
||||
void moduleCamera(scriptcontext_t *context) {
|
||||
assertNotNull(context, "Context cannot be NULL.");
|
||||
|
||||
// Create metatable for camera structure.
|
||||
if(luaL_newmetatable(context->luaState, "camera_mt")) {
|
||||
// Metatable methods
|
||||
lua_pushcfunction(context->luaState, moduleCameraIndex);
|
||||
lua_setfield(context->luaState, -2, "__index");
|
||||
lua_pushcfunction(context->luaState, moduleCameraNewIndex);
|
||||
lua_setfield(context->luaState, -2, "__newindex");
|
||||
lua_pop(context->luaState, 1);
|
||||
}
|
||||
|
||||
// Definitions
|
||||
#define MODULE_CAMERA_SCRIPT_LEN 64
|
||||
char_t buffer[MODULE_CAMERA_SCRIPT_LEN];
|
||||
#define MODULE_CAMERA_DEF(str, val) \
|
||||
snprintf( \
|
||||
buffer, \
|
||||
MODULE_CAMERA_SCRIPT_LEN, \
|
||||
"%s = %d", \
|
||||
str, \
|
||||
val \
|
||||
); \
|
||||
scriptContextExec(context, buffer);
|
||||
|
||||
MODULE_CAMERA_DEF(
|
||||
"CAMERA_PROJECTION_TYPE_PERSPECTIVE",
|
||||
CAMERA_PROJECTION_TYPE_PERSPECTIVE
|
||||
);
|
||||
MODULE_CAMERA_DEF(
|
||||
"CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED",
|
||||
CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
||||
);
|
||||
MODULE_CAMERA_DEF(
|
||||
"CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC",
|
||||
CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC
|
||||
);
|
||||
|
||||
MODULE_CAMERA_DEF(
|
||||
"CAMERA_VIEW_TYPE_MATRIX",
|
||||
CAMERA_VIEW_TYPE_MATRIX
|
||||
);
|
||||
MODULE_CAMERA_DEF(
|
||||
"CAMERA_VIEW_TYPE_LOOKAT",
|
||||
CAMERA_VIEW_TYPE_LOOKAT
|
||||
);
|
||||
MODULE_CAMERA_DEF(
|
||||
"CAMERA_VIEW_TYPE_2D",
|
||||
CAMERA_VIEW_TYPE_2D
|
||||
);
|
||||
MODULE_CAMERA_DEF(
|
||||
"CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT",
|
||||
CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT
|
||||
);
|
||||
|
||||
// Methods
|
||||
lua_register(context->luaState, "cameraCreate", moduleCameraCreate);
|
||||
lua_register(
|
||||
context->luaState,
|
||||
"cameraGetProjectionMatrix",
|
||||
moduleCameraGetProjectionMatrix
|
||||
);
|
||||
lua_register(
|
||||
context->luaState,
|
||||
"cameraGetViewMatrix",
|
||||
moduleCameraGetViewMatrix
|
||||
);
|
||||
}
|
||||
|
||||
int moduleCameraCreate(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.");
|
||||
|
||||
// If we are provided a projection type, use it.
|
||||
cameraprojectiontype_t projType = CAMERA_PROJECTION_TYPE_PERSPECTIVE;
|
||||
if(lua_gettop(L) >= 1) {
|
||||
if(!lua_isnumber(L, 1)) {
|
||||
luaL_error(L, "Camera projection type must be a number.");
|
||||
return 0;
|
||||
}
|
||||
projType = (cameraprojectiontype_t)lua_tonumber(L, 1);
|
||||
}
|
||||
|
||||
// Create camera that Lua will own
|
||||
camera_t *cam = (camera_t *)lua_newuserdata(L, sizeof(camera_t));
|
||||
memoryZero(cam, sizeof(camera_t));
|
||||
|
||||
// Set metatable
|
||||
luaL_getmetatable(L, "camera_mt");
|
||||
lua_setmetatable(L, -2);
|
||||
|
||||
// Init camera
|
||||
switch(projType) {
|
||||
case CAMERA_PROJECTION_TYPE_PERSPECTIVE:
|
||||
case CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED:
|
||||
cameraInitPerspective(cam);
|
||||
cam->projType = projType;
|
||||
break;
|
||||
|
||||
case CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC:
|
||||
cameraInitOrthographic(cam);
|
||||
cam->projType = projType;
|
||||
break;
|
||||
|
||||
default:
|
||||
luaL_error(L, "Invalid camera projection type specified.");
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleCameraIndex(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.");
|
||||
|
||||
camera_t *cam = (camera_t *)luaL_checkudata(l, 1, "camera_mt");
|
||||
assertNotNull(cam, "Camera pointer cannot be NULL.");
|
||||
|
||||
if(stringCompare(key, "near") == 0) {
|
||||
lua_pushnumber(l, cam->nearClip);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "far") == 0) {
|
||||
lua_pushnumber(l, cam->farClip);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "nearClip") == 0) {
|
||||
lua_pushnumber(l, cam->nearClip);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "farClip") == 0) {
|
||||
lua_pushnumber(l, cam->farClip);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Perspective relative values
|
||||
if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||
if(stringCompare(key, "left") == 0) {
|
||||
lua_pushnumber(l, cam->orthographic.left);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "right") == 0) {
|
||||
lua_pushnumber(l, cam->orthographic.right);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "top") == 0) {
|
||||
lua_pushnumber(l, cam->orthographic.top);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "bottom") == 0) {
|
||||
lua_pushnumber(l, cam->orthographic.bottom);
|
||||
return 1;
|
||||
}
|
||||
} else if(
|
||||
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
|
||||
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
||||
) {
|
||||
if(stringCompare(key, "fov") == 0) {
|
||||
lua_pushnumber(l, cam->perspective.fov);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// View type relative values.
|
||||
if(cam->viewType == CAMERA_VIEW_TYPE_MATRIX) {
|
||||
|
||||
} else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) {
|
||||
if(stringCompare(key, "position") == 0) {
|
||||
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
|
||||
memoryCopy(v, &cam->lookat.position, sizeof(vec3));
|
||||
luaL_getmetatable(l, "vec3_mt");
|
||||
lua_setmetatable(l, -2);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "target") == 0) {
|
||||
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
|
||||
memoryCopy(v, &cam->lookat.target, sizeof(vec3));
|
||||
luaL_getmetatable(l, "vec3_mt");
|
||||
lua_setmetatable(l, -2);
|
||||
return 1;
|
||||
} else if(stringCompare(key, "up") == 0) {
|
||||
vec3 *v = (vec3 *)lua_newuserdata(l, sizeof(vec3));
|
||||
memoryCopy(v, &cam->lookat.up, sizeof(vec3));
|
||||
luaL_getmetatable(l, "vec3_mt");
|
||||
lua_setmetatable(l, -2);
|
||||
return 1;
|
||||
}
|
||||
} else if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) {
|
||||
|
||||
} else if(cam->viewType == CAMERA_VIEW_TYPE_2D) {
|
||||
|
||||
}
|
||||
|
||||
lua_pushnil(l);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleCameraNewIndex(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.");
|
||||
|
||||
camera_t *cam = (camera_t *)luaL_checkudata(l, 1, "camera_mt");
|
||||
assertNotNull(cam, "Camera pointer cannot be NULL.");
|
||||
|
||||
if(stringCompare(key, "near") == 0 || stringCompare(key, "nearClip") == 0) {
|
||||
if(!lua_isnumber(l, 3)) {
|
||||
luaL_error(l, "Camera near clip must be a number.");
|
||||
}
|
||||
cam->nearClip = (float_t)lua_tonumber(l, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(stringCompare(key, "far") == 0 || stringCompare(key, "farClip") == 0) {
|
||||
if(!lua_isnumber(l, 3)) {
|
||||
luaL_error(l, "Camera far clip must be a number.");
|
||||
}
|
||||
cam->farClip = (float_t)lua_tonumber(l, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Perspective relative values
|
||||
if(cam->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
|
||||
if(stringCompare(key, "left") == 0) {
|
||||
if(!lua_isnumber(l, 3)) {
|
||||
luaL_error(l, "Camera orthographic left must be a number.");
|
||||
}
|
||||
cam->orthographic.left = (float_t)lua_tonumber(l, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(stringCompare(key, "right") == 0) {
|
||||
if(!lua_isnumber(l, 3)) {
|
||||
luaL_error(l, "Camera orthographic right must be a number.");
|
||||
}
|
||||
cam->orthographic.right = (float_t)lua_tonumber(l, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(stringCompare(key, "top") == 0) {
|
||||
if(!lua_isnumber(l, 3)) {
|
||||
luaL_error(l, "Camera orthographic top must be a number.");
|
||||
}
|
||||
cam->orthographic.top = (float_t)lua_tonumber(l, 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(stringCompare(key, "bottom") == 0) {
|
||||
if(!lua_isnumber(l, 3)) {
|
||||
luaL_error(l, "Camera orthographic bottom must be a number.");
|
||||
}
|
||||
cam->orthographic.bottom = (float_t)lua_tonumber(l, 3);
|
||||
return 0;
|
||||
}
|
||||
} else if(
|
||||
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
|
||||
cam->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
|
||||
) {
|
||||
if(stringCompare(key, "fov") == 0) {
|
||||
if(!lua_isnumber(l, 3)) {
|
||||
luaL_error(l, "Camera perspective FOV must be a number.");
|
||||
}
|
||||
cam->perspective.fov = (float_t)lua_tonumber(l, 3);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(cam->viewType == CAMERA_VIEW_TYPE_LOOKAT) {
|
||||
if(stringCompare(key, "position") == 0) {
|
||||
vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt");
|
||||
assertNotNull(v, "Vec3 pointer cannot be NULL.");
|
||||
memoryCopy(&cam->lookat.position, v, sizeof(vec3));
|
||||
return 0;
|
||||
} else if(stringCompare(key, "target") == 0) {
|
||||
vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt");
|
||||
assertNotNull(v, "Vec3 pointer cannot be NULL.");
|
||||
memoryCopy(&cam->lookat.target, v, sizeof(vec3));
|
||||
return 0;
|
||||
} else if(stringCompare(key, "up") == 0) {
|
||||
vec3 *v = (vec3 *)luaL_checkudata(l, 3, "vec3_mt");
|
||||
assertNotNull(v, "Vec3 pointer cannot be NULL.");
|
||||
memoryCopy(&cam->lookat.up, v, sizeof(vec3));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int moduleCameraGetProjectionMatrix(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL.");
|
||||
|
||||
camera_t *cam = (camera_t *)luaL_checkudata(L, 1, "camera_mt");
|
||||
assertNotNull(cam, "Camera pointer cannot be NULL.");
|
||||
|
||||
// Create mat4
|
||||
mat4 test;
|
||||
cameraGetProjectionMatrix(cam, test);
|
||||
|
||||
// Lua needs to own this matrix now
|
||||
mat4 *m = (mat4 *)lua_newuserdata(L, sizeof(mat4));
|
||||
memoryCopy(m, test, sizeof(mat4));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleCameraGetViewMatrix(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL.");
|
||||
|
||||
camera_t *cam = (camera_t *)luaL_checkudata(L, 1, "camera_mt");
|
||||
assertNotNull(cam, "Camera pointer cannot be NULL.");
|
||||
|
||||
// Create mat4
|
||||
mat4 test;
|
||||
cameraGetViewMatrix(cam, test);
|
||||
|
||||
// Lua needs to own this matrix now
|
||||
mat4 *m = (mat4 *)lua_newuserdata(L, sizeof(mat4));
|
||||
memoryCopy(m, test, sizeof(mat4));
|
||||
return 1;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
|
||||
/**
|
||||
* Register camera functions to the given script context.
|
||||
*
|
||||
* @param context The script context to register camera functions to.
|
||||
*/
|
||||
void moduleCamera(scriptcontext_t *context);
|
||||
|
||||
/**
|
||||
* Script binding for creating a new camera.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleCameraCreate(lua_State *L);
|
||||
|
||||
/**
|
||||
* Getter for camera structure fields.
|
||||
*
|
||||
* @param l The Lua state.
|
||||
*/
|
||||
int moduleCameraIndex(lua_State *l);
|
||||
|
||||
/**
|
||||
* Setter for camera structure fields.
|
||||
*
|
||||
* @param l The Lua state.
|
||||
*/
|
||||
int moduleCameraNewIndex(lua_State *l);
|
||||
|
||||
/**
|
||||
* Script binding for getting a camera's projection matrix.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleCameraGetProjectionMatrix(lua_State *L);
|
||||
|
||||
/**
|
||||
* Script binding for getting a camera's view matrix.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleCameraGetViewMatrix(lua_State *L);
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "assert/assert.h"
|
||||
#include "display/shader/shader.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
#include "display/camera/camera.h"
|
||||
|
||||
void moduleShader(scriptcontext_t *context) {
|
||||
assertNotNull(context, "Context cannot be NULL.");
|
||||
|
||||
@@ -30,6 +30,10 @@ void moduleTexture(scriptcontext_t *ctx) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#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");
|
||||
@@ -24,7 +25,7 @@ void moduleTileset(scriptcontext_t *ctx) {
|
||||
}
|
||||
lua_pop(ctx->luaState, 1); // Pop the metatable
|
||||
|
||||
// lua_register(ctx->luaState, "tilesetGetByName", moduleTilesetGetByName);
|
||||
lua_register(ctx->luaState, "tilesetLoad", moduleTilesetLoad);
|
||||
lua_register(ctx->luaState, "tilesetTileGetUV", moduleTilesetTileGetUV);
|
||||
lua_register(
|
||||
ctx->luaState, "tilesetPositionGetUV", moduleTilesetPositionGetUV
|
||||
@@ -71,34 +72,6 @@ int moduleTilesetToString(lua_State *l) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// int moduleTilesetGetByName(lua_State *l) {
|
||||
// assertNotNull(l, "Lua state cannot be NULL.");
|
||||
|
||||
// if(!lua_isstring(l, 1)) {
|
||||
// lua_pushnil(l);
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// const char_t *name = lua_tostring(l, 1);
|
||||
// if(name == NULL || name[0] == '\0') {
|
||||
// luaL_error(l, "Invalid tileset name.");
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// const tileset_t *ts = tilesetGetByName(name);
|
||||
// if(ts == NULL) {
|
||||
// lua_pushnil(l);
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// // Lua does not own this reference
|
||||
// lua_pushlightuserdata(l, (void *)ts);
|
||||
// luaL_getmetatable(l, "tileset_mt");
|
||||
// lua_setmetatable(l, -2);
|
||||
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
int moduleTilesetTileGetUV(lua_State *l) {
|
||||
assertNotNull(l, "Lua state cannot be NULL.");
|
||||
|
||||
@@ -157,4 +130,35 @@ int moduleTilesetPositionGetUV(lua_State *l) {
|
||||
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;
|
||||
}
|
||||
@@ -31,14 +31,6 @@ int moduleTilesetIndex(lua_State *l);
|
||||
*/
|
||||
int moduleTilesetToString(lua_State *l);
|
||||
|
||||
/**
|
||||
* Lua function to get a tileset by name.
|
||||
*
|
||||
* @param l The Lua state.
|
||||
* @return The number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleTilesetGetByName(lua_State *l);
|
||||
|
||||
/**
|
||||
* Lua function to get the UV coordinates for a tile index in a tileset.
|
||||
*
|
||||
@@ -53,4 +45,12 @@ int moduleTilesetTileGetUV(lua_State *l);
|
||||
* @param l The Lua state.
|
||||
* @return The number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleTilesetPositionGetUV(lua_State *l);
|
||||
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);
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "script/module/event/moduleevent.h"
|
||||
#include "script/module/display/modulecolor.h"
|
||||
#include "script/module/display/modulespritebatch.h"
|
||||
#include "script/module/display/modulecamera.h"
|
||||
#include "script/module/display/moduleglm.h"
|
||||
#include "script/module/display/moduleshader.h"
|
||||
#include "script/module/ui/moduleui.h"
|
||||
@@ -36,7 +35,6 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = {
|
||||
{ .name = "time", .callback = moduleTime },
|
||||
{ .name = "event", .callback = moduleEvent },
|
||||
{ .name = "spritebatch", .callback = moduleSpriteBatch },
|
||||
{ .name = "camera", .callback = moduleCamera },
|
||||
{ .name = "glm", .callback = moduleGLM },
|
||||
{ .name = "ui", .callback = moduleUi },
|
||||
{ .name = "text", .callback = moduleText },
|
||||
|
||||
+9
-9
@@ -16,9 +16,9 @@ ui_t UI;
|
||||
errorret_t uiInit(void) {
|
||||
memoryZero(&UI, sizeof(ui_t));
|
||||
|
||||
cameraInitOrthographic(&UI.camera);
|
||||
UI.camera.orthographic.left = 0;
|
||||
UI.camera.orthographic.bottom = 0;
|
||||
// cameraInitOrthographic(&UI.camera);
|
||||
// UI.camera.orthographic.left = 0;
|
||||
// UI.camera.orthographic.bottom = 0;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -27,14 +27,14 @@ void uiUpdate(void) {
|
||||
}
|
||||
|
||||
void uiRender(void) {
|
||||
UI.camera.orthographic.right = SCREEN.width;
|
||||
UI.camera.orthographic.top = SCREEN.height;
|
||||
// UI.camera.orthographic.right = SCREEN.width;
|
||||
// UI.camera.orthographic.top = SCREEN.height;
|
||||
|
||||
// cameraPushMatrix(&UI.camera);
|
||||
spriteBatchClear();
|
||||
// // cameraPushMatrix(&UI.camera);
|
||||
// spriteBatchClear();
|
||||
|
||||
spriteBatchFlush();
|
||||
// cameraPopMatrix();
|
||||
// spriteBatchFlush();
|
||||
// // cameraPopMatrix();
|
||||
}
|
||||
|
||||
void uiDispose(void) {
|
||||
|
||||
+1
-2
@@ -6,11 +6,10 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "display/camera/camera.h"
|
||||
#include "error/error.h"
|
||||
|
||||
typedef struct {
|
||||
camera_t camera;
|
||||
void *nothing;
|
||||
} ui_t;
|
||||
|
||||
extern ui_t UI;
|
||||
|
||||
@@ -11,7 +11,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(camera)
|
||||
add_subdirectory(framebuffer)
|
||||
add_subdirectory(mesh)
|
||||
add_subdirectory(texture)
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/camera/camera.h"
|
||||
#include "display/framebuffer/framebuffer.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
void cameraPushMatrixDolphin(camera_t *camera) {
|
||||
assertNotNull(camera, "Camera cannot be null");
|
||||
assertTrue(
|
||||
camera->nearClip > 0.0f,
|
||||
"Camera near clip must be greater than 0 for Dolphin"
|
||||
);
|
||||
|
||||
Mtx44 guProjection;
|
||||
Mtx guView;
|
||||
Mtx modelView;
|
||||
|
||||
switch(camera->projType) {
|
||||
case CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC:
|
||||
guOrtho(
|
||||
guProjection,
|
||||
camera->orthographic.top,
|
||||
camera->orthographic.bottom,
|
||||
camera->orthographic.left,
|
||||
camera->orthographic.right,
|
||||
camera->nearClip,
|
||||
camera->farClip
|
||||
);
|
||||
break;
|
||||
|
||||
case CAMERA_PROJECTION_TYPE_PERSPECTIVE:
|
||||
guPerspective(
|
||||
guProjection,
|
||||
// FOV is in degrees.
|
||||
camera->perspective.fov * (180.0f / GLM_PIf),
|
||||
(float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND) /
|
||||
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND),
|
||||
camera->nearClip,
|
||||
camera->farClip
|
||||
);
|
||||
break;
|
||||
|
||||
case CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED:
|
||||
assertUnreachable("Flipped perspective not implemented on Dolphin");
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid camera projection type");
|
||||
}
|
||||
|
||||
switch(camera->viewType) {
|
||||
case CAMERA_VIEW_TYPE_LOOKAT:
|
||||
guVector eye = {
|
||||
camera->lookat.position[0],
|
||||
camera->lookat.position[1],
|
||||
camera->lookat.position[2]
|
||||
};
|
||||
guVector up = {
|
||||
camera->lookat.up[0],
|
||||
camera->lookat.up[1],
|
||||
camera->lookat.up[2]
|
||||
};
|
||||
guVector look = {
|
||||
camera->lookat.target[0],
|
||||
camera->lookat.target[1],
|
||||
camera->lookat.target[2]
|
||||
};
|
||||
guLookAt(guView, &eye, &up, &look);
|
||||
break;
|
||||
|
||||
case CAMERA_VIEW_TYPE_MATRIX:
|
||||
assertUnreachable("Matrix camera not implemented");
|
||||
break;
|
||||
|
||||
case CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT:
|
||||
assertUnreachable("Pixel perfect camera not implemented");
|
||||
break;
|
||||
|
||||
case CAMERA_VIEW_TYPE_2D:
|
||||
guMtxIdentity(guView);
|
||||
guMtxTrans(guView, -camera->_2d.position[0], -camera->_2d.position[1], 0.0f);
|
||||
guMtxScale(guView, camera->_2d.zoom, camera->_2d.zoom, 1.0f);
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid camera view type");
|
||||
}
|
||||
|
||||
// Set Projection Matrix
|
||||
GX_LoadProjectionMtx(
|
||||
guProjection,
|
||||
camera->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC ?
|
||||
GX_ORTHOGRAPHIC :
|
||||
GX_PERSPECTIVE
|
||||
);
|
||||
|
||||
// Set view and model matrix. Dunno how I'll handle models but whatever.
|
||||
guMtxIdentity(modelView);
|
||||
guMtxTransApply(modelView, modelView, 0.0F, 0.0F, 0.0F);
|
||||
guMtxConcat(guView,modelView,modelView);
|
||||
GX_LoadPosMtxImm(modelView, GX_PNMTX0);
|
||||
}
|
||||
|
||||
void cameraPopMatrixDolphin(void) {
|
||||
|
||||
}
|
||||
@@ -1,23 +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"
|
||||
|
||||
typedef struct camera_s camera_t;
|
||||
|
||||
/**
|
||||
* Pushes the camera's transformation matrix onto the graphics stack.
|
||||
*
|
||||
* @param camera The camera to push the matrix of.
|
||||
*/
|
||||
void cameraPushMatrixDolphin(camera_t *camera);
|
||||
|
||||
/**
|
||||
* Pops the camera's transformation matrix from the graphics stack.
|
||||
*/
|
||||
void cameraPopMatrixDolphin(void);
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "cameradolphin.h"
|
||||
|
||||
#define cameraPushMatrixPlatform cameraPushMatrixDolphin
|
||||
#define cameraPopMatrixPlatform cameraPopMatrixDolphin
|
||||
@@ -76,7 +76,7 @@ errorret_t displayInitDolphin(void) {
|
||||
);
|
||||
|
||||
// Setup cull modes
|
||||
GX_SetCullMode(GX_CULL_NONE);
|
||||
GX_SetCullMode(GX_CULL_FRONT);
|
||||
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
|
||||
GX_SetZMode(GX_TRUE, GX_ALWAYS, GX_FALSE);
|
||||
GX_SetDispCopyGamma(GX_GM_1_0);
|
||||
|
||||
@@ -22,6 +22,8 @@ errorret_t shaderInitDolphin(
|
||||
|
||||
memoryZero(shader, sizeof(shaderdolphin_t));
|
||||
|
||||
shader->definition = def;
|
||||
|
||||
glm_mat4_identity(shader->view);
|
||||
glm_mat4_identity(shader->proj);
|
||||
glm_mat4_identity(shader->model);
|
||||
@@ -87,22 +89,19 @@ errorret_t shaderSetTextureDolphin(
|
||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
||||
|
||||
if(texture == NULL) {
|
||||
// GX_SetNumChans(0);
|
||||
GX_SetNumChans(1);
|
||||
GX_SetChanCtrl(
|
||||
GX_COLOR0A0,
|
||||
GX_DISABLE,
|
||||
GX_SRC_REG,
|
||||
GX_SRC_VTX,
|
||||
GX_SRC_REG,
|
||||
GX_LIGHTNULL,
|
||||
GX_DF_NONE,
|
||||
GX_AF_NONE
|
||||
);
|
||||
GX_SetChanAmbColor(GX_COLOR0A0, (GXColor){ 0, 0, 0, 0 });
|
||||
GX_SetChanMatColor(GX_COLOR0A0, (GXColor){ 255, 255, 255, 255 });
|
||||
GX_SetChanAmbColor(GX_COLOR0A0, (GXColor){0,0,0,0});
|
||||
|
||||
GX_SetNumTexGens(0);
|
||||
|
||||
GX_SetNumTevStages(1);
|
||||
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, GX_TEXMAP_NULL, GX_COLOR0A0);
|
||||
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
|
||||
@@ -117,13 +116,13 @@ errorret_t shaderSetTextureDolphin(
|
||||
GX_LoadTexObj(&texture->texObj, GX_TEXMAP0);
|
||||
GX_SetNumChans(1);
|
||||
GX_SetChanCtrl(
|
||||
GX_COLOR0A0,// Store in color channel 0
|
||||
GX_DISABLE,// Lighting disabled
|
||||
GX_SRC_REG,// Ambient color?
|
||||
GX_SRC_VTX,// Material color?
|
||||
GX_LIGHTNULL,// Light Mask
|
||||
GX_DF_NONE,// Diffuse function
|
||||
GX_AF_NONE// Attenuation function
|
||||
GX_COLOR0A0,
|
||||
GX_DISABLE,
|
||||
GX_SRC_REG,
|
||||
GX_SRC_REG,
|
||||
GX_LIGHTNULL,
|
||||
GX_DF_NONE,
|
||||
GX_AF_NONE
|
||||
);
|
||||
|
||||
// One set of UVs
|
||||
@@ -159,6 +158,25 @@ errorret_t shaderSetTextureDolphin(
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t shaderSetColorDolphin(
|
||||
shaderdolphin_t *shader,
|
||||
const char_t *name,
|
||||
color_t color
|
||||
) {
|
||||
assertNotNull(shader, "Shader must not be null");
|
||||
assertNotNull(name, "Uniform name must not be null");
|
||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
||||
|
||||
GX_SetChanMatColor(GX_COLOR0A0, (GXColor){
|
||||
color.r,
|
||||
color.g,
|
||||
color.b,
|
||||
color.a
|
||||
});
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t shaderDisposeDolphin(shaderdolphin_t *shader) {
|
||||
assertNotNull(shader, "Shader must not be null");
|
||||
|
||||
|
||||
@@ -8,7 +8,19 @@
|
||||
#pragma once
|
||||
#include "display/texture/texture.h"
|
||||
|
||||
typedef union shadermaterial_u shadermaterial_t;
|
||||
typedef struct shaderdolphin_s shaderdolphin_t;
|
||||
|
||||
typedef struct {
|
||||
errorret_t (*setMaterial)(
|
||||
shaderdolphin_t *shader,
|
||||
const shadermaterial_t *material
|
||||
);
|
||||
} shaderdefinitiondolphin_t;
|
||||
|
||||
typedef struct shaderdolphin_s {
|
||||
shaderdefinitiondolphin_t *definition;
|
||||
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 model;
|
||||
@@ -22,10 +34,6 @@ typedef struct {
|
||||
uint_fast8_t dirtyMatrix;
|
||||
} shaderdolphin_t;
|
||||
|
||||
typedef struct {
|
||||
void *empty;
|
||||
} shaderdefinitiondolphin_t;
|
||||
|
||||
#define SHADER_DOLPHIN_DIRTY_MODEL (1 << 0)
|
||||
#define SHADER_DOLPHIN_DIRTY_PROJ (1 << 1)
|
||||
#define SHADER_DOLPHIN_DIRTY_VIEW (1 << 2)
|
||||
@@ -80,9 +88,23 @@ errorret_t shaderSetTextureDolphin(
|
||||
texture_t *texture
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets a color uniform in the dolphin shader.
|
||||
*
|
||||
* @param shader Shader to set the color in.
|
||||
* @param name Name of the uniform to set.
|
||||
* @param color Color to set.
|
||||
* @return Error code if failure.
|
||||
*/
|
||||
errorret_t shaderSetColorDolphin(
|
||||
shaderdolphin_t *shader,
|
||||
const char_t *name,
|
||||
color_t color
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes a dolphin shader. Basically does nothing.
|
||||
*
|
||||
*
|
||||
* @param shader Shader to dispose.
|
||||
* @return Error code if failure.
|
||||
*/
|
||||
|
||||
@@ -15,4 +15,5 @@ typedef shaderdefinitiondolphin_t shaderdefinitionplatform_t;
|
||||
#define shaderBindPlatform shaderBindDolphin
|
||||
#define shaderSetMatrixPlatform shaderSetMatrixDolphin
|
||||
#define shaderSetTexturePlatform shaderSetTextureDolphin
|
||||
#define shaderSetColorPlatform shaderSetColorDolphin
|
||||
#define shaderDisposePlatform shaderDisposeDolphin
|
||||
@@ -8,5 +8,5 @@
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
shaderdefinition_t SHADER_UNLIT_DEFINITION = {
|
||||
0
|
||||
.setMaterial = shaderUnlitSetMaterial
|
||||
};
|
||||
@@ -10,7 +10,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(camera)
|
||||
add_subdirectory(framebuffer)
|
||||
add_subdirectory(texture)
|
||||
add_subdirectory(mesh)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
cameragl.c
|
||||
)
|
||||
@@ -1,135 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/camera/camera.h"
|
||||
#include "display/framebuffer/framebuffer.h"
|
||||
#include "display/screen/screen.h"
|
||||
#include "assert/assertgl.h"
|
||||
|
||||
void cameraPushMatrixGL(camera_t *camera) {
|
||||
assertNotNull(camera, "Not a camera component");
|
||||
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
|
||||
switch(camera->projType) {
|
||||
case CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC: {
|
||||
assertTrue(
|
||||
camera->orthographic.right != camera->orthographic.left &&
|
||||
camera->orthographic.top != camera->orthographic.bottom,
|
||||
"Invalid orthographic projection parameters"
|
||||
);
|
||||
glm_ortho(
|
||||
camera->orthographic.left,
|
||||
camera->orthographic.right,
|
||||
camera->orthographic.bottom,
|
||||
camera->orthographic.top,
|
||||
camera->nearClip,
|
||||
camera->farClip,
|
||||
projection
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED:
|
||||
case CAMERA_PROJECTION_TYPE_PERSPECTIVE: {
|
||||
const float_t aspect = (
|
||||
(float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND) /
|
||||
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
|
||||
);
|
||||
glm_perspective(
|
||||
camera->perspective.fov,
|
||||
aspect,
|
||||
camera->nearClip,
|
||||
camera->farClip,
|
||||
projection
|
||||
);
|
||||
|
||||
if(camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED) {
|
||||
// Flip Y axis
|
||||
projection[1][1] *= -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch(camera->viewType) {
|
||||
case CAMERA_VIEW_TYPE_MATRIX:
|
||||
glm_mat4_ucopy(camera->view, view);
|
||||
break;
|
||||
|
||||
case CAMERA_VIEW_TYPE_LOOKAT:
|
||||
glm_lookat(
|
||||
camera->lookat.position,
|
||||
camera->lookat.target,
|
||||
camera->lookat.up,
|
||||
view
|
||||
);
|
||||
break;
|
||||
|
||||
case CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT:
|
||||
assertTrue(
|
||||
camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
|
||||
camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED,
|
||||
"Pixel perfect camera view requires perspective projection"
|
||||
);
|
||||
|
||||
// const float_t viewportHeight = (
|
||||
// (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
|
||||
// );
|
||||
const float_t viewportHeight = (float_t)SCREEN.height;
|
||||
const float_t z = (viewportHeight / 2.0f) / (
|
||||
camera->lookatPixelPerfect.pixelsPerUnit *
|
||||
tanf(camera->perspective.fov / 2.0f)
|
||||
);
|
||||
|
||||
vec3 position;
|
||||
glm_vec3_copy(camera->lookatPixelPerfect.target, position);
|
||||
glm_vec3_add(position, camera->lookatPixelPerfect.offset, position);
|
||||
position[2] += z;
|
||||
glm_lookat(
|
||||
position,
|
||||
camera->lookatPixelPerfect.target,
|
||||
camera->lookatPixelPerfect.up,
|
||||
view
|
||||
);
|
||||
break;
|
||||
|
||||
case CAMERA_VIEW_TYPE_2D:
|
||||
glm_mat4_identity(view);
|
||||
glm_translate(view, (vec3){
|
||||
-camera->_2d.position[0],
|
||||
-camera->_2d.position[1],
|
||||
0.0f
|
||||
});
|
||||
glm_scale(view, (vec3){
|
||||
camera->_2d.zoom,
|
||||
camera->_2d.zoom,
|
||||
1.0f
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid camera view type");
|
||||
}
|
||||
|
||||
// glPushMatrix();
|
||||
// glMatrixMode(GL_PROJECTION);
|
||||
// glLoadIdentity();
|
||||
// glLoadMatrixf((const GLfloat*)projection);
|
||||
assertNoGLError("Failed to set projection matrix");
|
||||
|
||||
// glMatrixMode(GL_MODELVIEW);
|
||||
// glLoadIdentity();
|
||||
// glLoadMatrixf((const GLfloat*)view);
|
||||
assertNoGLError("Failed to set view matrix");
|
||||
}
|
||||
|
||||
void cameraPopMatrixGL(void) {
|
||||
// glPopMatrix();
|
||||
assertNoGLError("Failed to pop camera matrix");
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct camera_s camera_t;
|
||||
|
||||
/**
|
||||
* Initializes a camera with default perspective parameters.
|
||||
*
|
||||
* @param camera The camera to initialize.
|
||||
*/
|
||||
void cameraPushMatrixGL(camera_t *camera);
|
||||
|
||||
/**
|
||||
* Removes the previously pushed camera matrix off the matrix stack.
|
||||
*/
|
||||
void cameraPopMatrixGL(void);
|
||||
@@ -153,11 +153,7 @@ errorret_t meshDrawGL(
|
||||
);
|
||||
|
||||
// Shader may have model matrix here
|
||||
#ifdef DUSK_OPENGL_LEGACY
|
||||
errorChain(shaderLegacyMatrixUpdate());
|
||||
#endif
|
||||
|
||||
// glDrawArrays(mesh->primitiveType, offset, count);
|
||||
errorChain(shaderLegacyMatrixUpdate());
|
||||
glDrawArrays(mesh->primitiveType, 0, count);
|
||||
errorChain(errorGLCheck());
|
||||
#else
|
||||
|
||||
@@ -20,6 +20,8 @@ errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) {
|
||||
assertNotNull(def, "Shader definition cannot be null");
|
||||
memoryZero(shader, sizeof(shadergl_t));
|
||||
|
||||
shader->definition = def;
|
||||
|
||||
#ifdef DUSK_OPENGL_LEGACY
|
||||
glm_mat4_identity(shader->view);
|
||||
glm_mat4_identity(shader->proj);
|
||||
@@ -31,8 +33,6 @@ errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) {
|
||||
assertNotNull(def->vert, "Vertex shader source cannot be null");
|
||||
assertNotNull(def->frag, "Fragment shader source cannot be null");
|
||||
|
||||
shader->setTexture = def->setTexture;
|
||||
|
||||
// Create vertex shader
|
||||
shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
|
||||
errorret_t err = errorGLCheck();
|
||||
@@ -223,6 +223,9 @@ errorret_t shaderSetTextureGL(
|
||||
"Only one texture supported in legacy opengl."
|
||||
);
|
||||
|
||||
// glActiveTexture(GL_TEXTURE0);
|
||||
errorChain(errorGLCheck());
|
||||
|
||||
if(texture == NULL) {
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
errorChain(errorGLCheck());
|
||||
@@ -233,14 +236,92 @@ errorret_t shaderSetTextureGL(
|
||||
errorChain(errorGLCheck());
|
||||
glBindTexture(GL_TEXTURE_2D, texture->id);
|
||||
errorChain(errorGLCheck());
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
errorChain(errorGLCheck());
|
||||
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PRIMARY_COLOR);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
|
||||
// errorChain(errorGLCheck());
|
||||
|
||||
#else
|
||||
if(shader->setTexture == NULL) {
|
||||
assertUnreachable("Shader does not support setting textures.");
|
||||
}
|
||||
errorChain(shader->setTexture(shader, name, texture));
|
||||
assertNotNull(shader->definition, "Shader definition cannot be null");
|
||||
assertNotNull(shader->definition->setTexture, "Shader cannot do textures.");
|
||||
errorChain(shader->definition->setTexture(shader, name, texture));
|
||||
#endif
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t shaderSetColorGL(
|
||||
shadergl_t *shader,
|
||||
const char_t *name,
|
||||
color_t color
|
||||
) {
|
||||
assertNotNull(shader, "Shader cannot be null");
|
||||
assertStrLenMin(name, 1, "Uniform name cannot be empty");
|
||||
|
||||
#ifdef DUSK_OPENGL_LEGACY
|
||||
// if(color.a == 0) {
|
||||
// glDisable(GL_TEXTURE_2D);
|
||||
// errorChain(errorGLCheck());
|
||||
// errorOk();
|
||||
// }
|
||||
|
||||
// glActiveTexture(GL_TEXTURE1);
|
||||
// errorChain(errorGLCheck());
|
||||
|
||||
// if(color.r == 255 && color.g == 255 && color.b == 255) {
|
||||
// glDisable(GL_TEXTURE_2D);
|
||||
// errorChain(errorGLCheck());
|
||||
// errorOk();
|
||||
// }
|
||||
|
||||
// glEnable(GL_TEXTURE_2D);
|
||||
// errorChain(errorGLCheck());
|
||||
// glBindTexture(GL_TEXTURE_2D, TEXTURE_WHITE.id);
|
||||
// errorChain(errorGLCheck());
|
||||
|
||||
// GLfloat tint[4] = {
|
||||
// ((float_t)color.r) / 255.0f,
|
||||
// ((float_t)color.g) / 255.0f,
|
||||
// ((float_t)color.b) / 255.0f,
|
||||
// ((float_t)color.a) / 255.0f
|
||||
// };
|
||||
// glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, tint);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_PREVIOUS);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
|
||||
// errorChain(errorGLCheck());
|
||||
// glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
|
||||
// errorChain(errorGLCheck());
|
||||
#else
|
||||
GLint location;
|
||||
errorChain(shaderParamGetLocationGL(shader, name, &location));
|
||||
glUniform4f(
|
||||
location,
|
||||
color.r / 255.0f,
|
||||
color.g / 255.0f,
|
||||
color.b / 255.0f,
|
||||
color.a / 255.0f
|
||||
);
|
||||
errorChain(errorGLCheck());
|
||||
#endif
|
||||
|
||||
errorOk();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "display/texture/texture.h"
|
||||
|
||||
typedef struct shadergl_s shadergl_t;
|
||||
typedef union shadermaterial_u shadermaterial_t;
|
||||
|
||||
typedef errorret_t (*shadersettexturefn_t)(
|
||||
shadergl_t *,
|
||||
@@ -17,7 +18,21 @@ typedef errorret_t (*shadersettexturefn_t)(
|
||||
texture_t *
|
||||
);
|
||||
|
||||
typedef struct {
|
||||
errorret_t (*setMaterial)(shadergl_t *, const shadermaterial_t *);
|
||||
|
||||
#ifdef DUSK_OPENGL_LEGACY
|
||||
#else
|
||||
errorret_t (*setTexture)(shadergl_t *, const char_t *, texture_t *);
|
||||
|
||||
const char_t *vert;
|
||||
const char_t *frag;
|
||||
#endif
|
||||
} shaderdefinitiongl_t;
|
||||
|
||||
typedef struct shadergl_s {
|
||||
const shaderdefinitiongl_t *definition;
|
||||
|
||||
#ifdef DUSK_OPENGL_LEGACY
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
@@ -26,21 +41,9 @@ typedef struct shadergl_s {
|
||||
GLuint shaderProgramId;
|
||||
GLuint vertexShaderId;
|
||||
GLuint fragmentShaderId;
|
||||
shadersettexturefn_t setTexture;
|
||||
#endif
|
||||
} shadergl_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
#ifdef DUSK_OPENGL_LEGACY
|
||||
void *nothing;
|
||||
#else
|
||||
const char_t *vert;
|
||||
const char_t *frag;
|
||||
shadersettexturefn_t setTexture;
|
||||
#endif
|
||||
} shaderdefinitiongl_t;
|
||||
|
||||
#if DUSK_OPENGL_LEGACY
|
||||
typedef struct {
|
||||
shadergl_t *boundShader;
|
||||
@@ -121,11 +124,11 @@ errorret_t shaderSetTextureGL(
|
||||
* @param color The color data to set.
|
||||
* @return An errorret_t indicating success or failure.
|
||||
*/
|
||||
// errorret_t shaderSetColorGL(
|
||||
// shadergl_t *shader,
|
||||
// const char_t *name,
|
||||
// color_t color
|
||||
// );
|
||||
errorret_t shaderSetColorGL(
|
||||
shadergl_t *shader,
|
||||
const char_t *name,
|
||||
color_t color
|
||||
);
|
||||
|
||||
/**
|
||||
* Disposes of a shader, freeing any associated resources.
|
||||
|
||||
@@ -15,5 +15,5 @@ typedef shaderdefinitiongl_t shaderdefinitionplatform_t;
|
||||
#define shaderBindPlatform shaderBindGL
|
||||
#define shaderSetMatrixPlatform shaderSetMatrixGL
|
||||
#define shaderSetTexturePlatform shaderSetTextureGL
|
||||
// #define shaderSetColorPlatform shaderSetColorGL
|
||||
#define shaderSetColorPlatform shaderSetColorGL
|
||||
#define shaderDisposePlatform shaderDisposeGL
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "assert/assertgl.h"
|
||||
|
||||
#ifdef DUSK_OPENGL_LEGACY
|
||||
shaderdefinition_t SHADER_UNLIT_DEFINITION = { 0 };
|
||||
shaderdefinition_t SHADER_UNLIT_DEFINITION = {
|
||||
.setMaterial = shaderUnlitSetMaterial,
|
||||
};
|
||||
#else
|
||||
errorret_t shaderUnlitSetTextureGL(
|
||||
shadergl_t *shader,
|
||||
@@ -75,7 +77,12 @@
|
||||
errorOk();
|
||||
}
|
||||
|
||||
|
||||
|
||||
shaderdefinition_t SHADER_UNLIT_DEFINITION = {
|
||||
.setMaterial = shaderUnlitSetMaterial,
|
||||
.setTexture = shaderUnlitSetTextureGL,
|
||||
|
||||
.vert =
|
||||
#ifdef DUSK_OPENGL_ES
|
||||
"#version 300 es\n"
|
||||
@@ -115,6 +122,8 @@
|
||||
" v_TexCoord = a_TexCoord;\n"
|
||||
"}\n",
|
||||
#endif
|
||||
|
||||
|
||||
.frag =
|
||||
#ifdef DUSK_OPENGL_ES
|
||||
"#version 300 es\n"
|
||||
@@ -124,6 +133,7 @@
|
||||
"uniform int u_TextureType;\n"
|
||||
"uniform uint u_Colors[256];\n"// For paletted textures.
|
||||
"uniform int u_ColorCount;\n"
|
||||
"uniform vec4 u_Color;\n"
|
||||
// Fragment shader inputs
|
||||
"in vec4 v_Color;\n"
|
||||
"in vec2 v_TexCoord;\n"
|
||||
@@ -131,11 +141,11 @@
|
||||
"out vec4 FragColor;\n"
|
||||
"void main() {\n"
|
||||
" if(u_TextureType == 0) {\n"// No texture
|
||||
" FragColor = v_Color;\n"
|
||||
" FragColor = v_Color * u_Color;\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" if(u_TextureType == 1) {\n"// Regular texture
|
||||
" FragColor = texture(u_Texture, v_TexCoord) * v_Color;\n"
|
||||
" FragColor = texture(u_Texture, v_TexCoord) * v_Color * u_Color;\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" if(u_TextureType == 2) {\n"// Paletted texture
|
||||
@@ -146,11 +156,10 @@
|
||||
" float g = float((palColor >> 16) & 0xFFu) / 255.0;\n"
|
||||
" float b = float((palColor >> 8) & 0xFFu) / 255.0;\n"
|
||||
" float a = float((palColor >> 0) & 0xFFu) / 255.0;\n"
|
||||
" vec4 paletteColor = vec4(r, g, b, a);\n"
|
||||
" FragColor = paletteColor;\n"
|
||||
" FragColor = vec4(r, g, b, a) * u_Color;\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" FragColor = v_Color;\n"// Unknown texture type?
|
||||
" FragColor = v_Color * u_Color;\n"// Unknown texture type?
|
||||
"}\n",
|
||||
#else
|
||||
"#version 330 core\n"
|
||||
@@ -159,6 +168,7 @@
|
||||
"uniform int u_TextureType;\n"
|
||||
"uniform uint u_Colors[256];\n"// For paletted textures.
|
||||
"uniform int u_ColorCount;\n"
|
||||
"uniform vec4 u_Color;\n"
|
||||
// Fragment shader inputs
|
||||
"in vec4 v_Color;\n"
|
||||
"in vec2 v_TexCoord;\n"
|
||||
@@ -166,11 +176,11 @@
|
||||
"out vec4 FragColor;\n"
|
||||
"void main() {\n"
|
||||
" if(u_TextureType == 0) {\n"// No texture
|
||||
" FragColor = v_Color;\n"
|
||||
" FragColor = v_Color * u_Color;\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" if(u_TextureType == 1) {\n"// Regular texture
|
||||
" FragColor = texture(u_Texture, v_TexCoord) * v_Color;\n"
|
||||
" FragColor = texture(u_Texture, v_TexCoord) * v_Color * u_Color;\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" if(u_TextureType == 2) {\n"// Paletted texture
|
||||
@@ -181,14 +191,11 @@
|
||||
" float g = float((palColor >> 16) & 0xFFu) / 255.0;\n"
|
||||
" float b = float((palColor >> 8) & 0xFFu) / 255.0;\n"
|
||||
" float a = float((palColor >> 0) & 0xFFu) / 255.0;\n"
|
||||
" vec4 paletteColor = vec4(r, g, b, a);\n"
|
||||
" FragColor = paletteColor;\n"
|
||||
" FragColor = vec4(r, g, b, a) * u_Color;\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" FragColor = v_Color;\n"// Unknown texture type?
|
||||
" FragColor = v_Color * u_Color;\n"// Unknown texture type?
|
||||
"}\n",
|
||||
#endif
|
||||
|
||||
.setTexture = shaderUnlitSetTextureGL
|
||||
};
|
||||
#endif
|
||||
@@ -10,7 +10,7 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(game)
|
||||
add_subdirectory(item)
|
||||
add_subdirectory(map)
|
||||
add_subdirectory(story)
|
||||
add_subdirectory(script)
|
||||
@@ -1,5 +1,5 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
#include "error/error.h"
|
||||
|
||||
/**
|
||||
* Initializes the game.
|
||||
* Initializes the game systems and loads the initial game state.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t gameInit(void);
|
||||
|
||||
/**
|
||||
* Updates the game state. Should be called every frame.
|
||||
* Updates the game state. This should be called every frame.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
errorret_t gameUpdate(void);
|
||||
|
||||
/**
|
||||
* Disposes of game resources. Should be called when the game is shutting down.
|
||||
* Disposes of game resources and shuts down the game systems.
|
||||
*
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
@@ -1,13 +0,0 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
mapchunk.c
|
||||
map.c
|
||||
worldpos.c
|
||||
maptile.c
|
||||
)
|
||||
@@ -1,272 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "map.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
#include "asset/asset.h"
|
||||
#include "display/texture/texture.h"
|
||||
// #include "entity/entity.h"
|
||||
#include "util/string.h"
|
||||
#include "time/time.h"
|
||||
|
||||
map_t MAP;
|
||||
|
||||
errorret_t mapInit() {
|
||||
memoryZero(&MAP, sizeof(map_t));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
bool_t mapIsLoaded() {
|
||||
return MAP.filePath[0] != '\0';
|
||||
}
|
||||
|
||||
errorret_t mapLoad(const char_t *path, const chunkpos_t position) {
|
||||
assertStrLenMin(path, 1, "Map file path cannot be empty");
|
||||
assertStrLenMax(path, MAP_FILE_PATH_MAX - 1, "Map file path too long");
|
||||
|
||||
if(stringCompare(MAP.filePath, path) == 0) {
|
||||
// Same map, no need to reload
|
||||
errorChain(mapPositionSet(position));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
chunkindex_t i;
|
||||
|
||||
// Unload all loaded chunks
|
||||
if(mapIsLoaded()) {
|
||||
for(i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
mapChunkUnload(&MAP.chunks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Store the map file path
|
||||
stringCopy(MAP.filePath, path, MAP_FILE_PATH_MAX);
|
||||
|
||||
// Determine directory path (it is dirname)
|
||||
stringCopy(MAP.dirPath, path, MAP_FILE_PATH_MAX);
|
||||
char_t *last = stringFindLastChar(MAP.dirPath, '/');
|
||||
if(last == NULL) errorThrow("Invalid map file path");
|
||||
|
||||
// Store filename, sans extension
|
||||
stringCopy(MAP.fileName, last + 1, MAP_FILE_PATH_MAX);
|
||||
*last = '\0'; // Terminate to get directory path
|
||||
|
||||
last = stringFindLastChar(MAP.fileName, '.');
|
||||
if(last == NULL) errorThrow("Map file name has no extension");
|
||||
*last = '\0'; // Terminate to remove extension
|
||||
|
||||
// Load map itself
|
||||
errorChain(assetLoad(MAP.filePath, &MAP));
|
||||
|
||||
// Reset map position
|
||||
MAP.chunkPosition = position;
|
||||
|
||||
// Perform "initial load"
|
||||
i = 0;
|
||||
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
||||
for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
|
||||
for(chunkunit_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
|
||||
mapchunk_t *chunk = &MAP.chunks[i];
|
||||
chunk->position.x = x + position.x;
|
||||
chunk->position.y = y + position.y;
|
||||
chunk->position.z = z + position.z;
|
||||
MAP.chunkOrder[i] = chunk;
|
||||
errorChain(mapChunkLoad(chunk));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t mapPositionSet(const chunkpos_t newPos) {
|
||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||
|
||||
const chunkpos_t curPos = MAP.chunkPosition;
|
||||
if(mapChunkPositionIsEqual(curPos, newPos)) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Determine which chunks remain loaded
|
||||
chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0};
|
||||
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
|
||||
|
||||
uint32_t remainingCount = 0;
|
||||
uint32_t freedCount = 0;
|
||||
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
// Will this chunk remain loaded?
|
||||
mapchunk_t *chunk = &MAP.chunks[i];
|
||||
if(
|
||||
chunk->position.x >= newPos.x &&
|
||||
chunk->position.x < newPos.x + MAP_CHUNK_WIDTH &&
|
||||
|
||||
chunk->position.y >= newPos.y &&
|
||||
chunk->position.y < newPos.y + MAP_CHUNK_HEIGHT &&
|
||||
|
||||
chunk->position.z >= newPos.z &&
|
||||
chunk->position.z < newPos.z + MAP_CHUNK_DEPTH
|
||||
) {
|
||||
// Stays loaded
|
||||
chunksRemaining[remainingCount++] = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not remaining loaded
|
||||
chunksFreed[freedCount++] = i;
|
||||
}
|
||||
|
||||
// Unload the freed chunks
|
||||
for(chunkindex_t i = 0; i < freedCount; i++) {
|
||||
mapchunk_t *chunk = &MAP.chunks[chunksFreed[i]];
|
||||
mapChunkUnload(chunk);
|
||||
}
|
||||
|
||||
// This can probably be optimized later, for now we check each chunk and see
|
||||
// if it needs loading or not, and update the chunk order
|
||||
chunkindex_t orderIndex = 0;
|
||||
for(chunkunit_t zOff = 0; zOff < MAP_CHUNK_DEPTH; zOff++) {
|
||||
for(chunkunit_t yOff = 0; yOff < MAP_CHUNK_HEIGHT; yOff++) {
|
||||
for(chunkunit_t xOff = 0; xOff < MAP_CHUNK_WIDTH; xOff++) {
|
||||
const chunkpos_t newChunkPos = {
|
||||
newPos.x + xOff, newPos.y + yOff, newPos.z + zOff
|
||||
};
|
||||
|
||||
// Is this chunk already loaded (was not unloaded earlier)?
|
||||
chunkindex_t chunkIndex = -1;
|
||||
for(chunkindex_t i = 0; i < remainingCount; i++) {
|
||||
mapchunk_t *chunk = &MAP.chunks[chunksRemaining[i]];
|
||||
if(!mapChunkPositionIsEqual(chunk->position, newChunkPos)) continue;
|
||||
chunkIndex = chunksRemaining[i];
|
||||
break;
|
||||
}
|
||||
|
||||
// Need to load this chunk
|
||||
if(chunkIndex == -1) {
|
||||
// Find a freed chunk to reuse
|
||||
chunkIndex = chunksFreed[--freedCount];
|
||||
mapchunk_t *chunk = &MAP.chunks[chunkIndex];
|
||||
chunk->position = newChunkPos;
|
||||
errorChain(mapChunkLoad(chunk));
|
||||
}
|
||||
|
||||
MAP.chunkOrder[orderIndex++] = &MAP.chunks[chunkIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update map position
|
||||
MAP.chunkPosition = newPos;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void mapUpdate() {
|
||||
#ifdef DUSK_TIME_DYNAMIC
|
||||
if(!TIME.dynamicUpdate) return;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mapRender() {
|
||||
if(!mapIsLoaded()) return;
|
||||
|
||||
// textureBind(NULL);
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
mapChunkRender(&MAP.chunks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void mapDispose() {
|
||||
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
|
||||
mapChunkUnload(&MAP.chunks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void mapChunkUnload(mapchunk_t* chunk) {
|
||||
// for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
||||
// if(chunk->entities[i] == 0xFF) break;
|
||||
// entity_t *entity = &ENTITIES[chunk->entities[i]];
|
||||
// entity->type = ENTITY_TYPE_NULL;
|
||||
// }
|
||||
|
||||
for(uint8_t i = 0; i < chunk->meshCount; i++) {
|
||||
// if(chunk->meshes[i].vertexCount == 0) continue;
|
||||
// meshDispose(&chunk->meshes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t mapChunkLoad(mapchunk_t* chunk) {
|
||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||
|
||||
char_t buffer[160];
|
||||
|
||||
// TODO: Can probably move this to asset load logic?
|
||||
chunk->meshCount = 0;
|
||||
memoryZero(chunk->meshes, sizeof(chunk->meshes));
|
||||
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
||||
|
||||
// Get chunk filepath.
|
||||
snprintf(buffer, sizeof(buffer), "%s/chunks/%d_%d_%d.dmc",
|
||||
MAP.dirPath,
|
||||
chunk->position.x,
|
||||
chunk->position.y,
|
||||
chunk->position.z
|
||||
);
|
||||
|
||||
// Chunk available?
|
||||
if(!assetFileExists(buffer)) {
|
||||
memoryZero(chunk->tiles, sizeof(chunk->tiles));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Load.
|
||||
errorChain(assetLoad(buffer, chunk));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) {
|
||||
if(!mapIsLoaded()) return -1;
|
||||
|
||||
chunkpos_t relPos = {
|
||||
position.x - MAP.chunkPosition.x,
|
||||
position.y - MAP.chunkPosition.y,
|
||||
position.z - MAP.chunkPosition.z
|
||||
};
|
||||
|
||||
if(
|
||||
relPos.x < 0 || relPos.y < 0 || relPos.z < 0 ||
|
||||
relPos.x >= MAP_CHUNK_WIDTH ||
|
||||
relPos.y >= MAP_CHUNK_HEIGHT ||
|
||||
relPos.z >= MAP_CHUNK_DEPTH
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return chunkPosToIndex(&relPos);
|
||||
}
|
||||
|
||||
mapchunk_t* mapGetChunk(const uint8_t index) {
|
||||
if(index >= MAP_CHUNK_COUNT) return NULL;
|
||||
if(!mapIsLoaded()) return NULL;
|
||||
return MAP.chunkOrder[index];
|
||||
}
|
||||
|
||||
maptile_t mapGetTile(const worldpos_t position) {
|
||||
if(!mapIsLoaded()) return TILE_SHAPE_NULL;
|
||||
|
||||
chunkpos_t chunkPos;
|
||||
worldPosToChunkPos(&position, &chunkPos);
|
||||
chunkindex_t chunkIndex = mapGetChunkIndexAt(chunkPos);
|
||||
if(chunkIndex == -1) return TILE_SHAPE_NULL;
|
||||
|
||||
mapchunk_t *chunk = mapGetChunk(chunkIndex);
|
||||
assertNotNull(chunk, "Chunk pointer cannot be NULL");
|
||||
chunktileindex_t tileIndex = worldPosToChunkTileIndex(&position);
|
||||
return chunk->tiles[tileIndex];
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "mapchunk.h"
|
||||
|
||||
#define MAP_FILE_PATH_MAX 128
|
||||
|
||||
typedef struct map_s {
|
||||
char_t filePath[MAP_FILE_PATH_MAX];
|
||||
char_t dirPath[MAP_FILE_PATH_MAX];
|
||||
char_t fileName[MAP_FILE_PATH_MAX];
|
||||
|
||||
mapchunk_t chunks[MAP_CHUNK_COUNT];
|
||||
mapchunk_t *chunkOrder[MAP_CHUNK_COUNT];
|
||||
chunkpos_t chunkPosition;
|
||||
} map_t;
|
||||
|
||||
extern map_t MAP;
|
||||
|
||||
/**
|
||||
* Initializes the map.
|
||||
*
|
||||
* @return An error code.
|
||||
*/
|
||||
errorret_t mapInit();
|
||||
|
||||
/**
|
||||
* Checks if a map is loaded.
|
||||
*
|
||||
* @return true if a map is loaded, false otherwise.
|
||||
*/
|
||||
bool_t mapIsLoaded();
|
||||
|
||||
/**
|
||||
* Loads a map from the given file path.
|
||||
*
|
||||
* @param path The file path.
|
||||
* @param position The initial chunk position.
|
||||
* @return An error code.
|
||||
*/
|
||||
errorret_t mapLoad(const char_t *path, const chunkpos_t position);
|
||||
|
||||
/**
|
||||
* Updates the map.
|
||||
*/
|
||||
void mapUpdate();
|
||||
|
||||
/**
|
||||
* Renders the map.
|
||||
*/
|
||||
void mapRender();
|
||||
|
||||
/**
|
||||
* Disposes of the map.
|
||||
*/
|
||||
void mapDispose();
|
||||
|
||||
/**
|
||||
* Sets the map position and updates chunks accordingly.
|
||||
*
|
||||
* @param newPos The new chunk position.
|
||||
* @return An error code.
|
||||
*/
|
||||
errorret_t mapPositionSet(const chunkpos_t newPos);
|
||||
|
||||
/**
|
||||
* Unloads a chunk.
|
||||
*
|
||||
* @param chunk The chunk to unload.
|
||||
*/
|
||||
void mapChunkUnload(mapchunk_t* chunk);
|
||||
|
||||
/**
|
||||
* Loads a chunk.
|
||||
*
|
||||
* @param chunk The chunk to load.
|
||||
* @return An error code.
|
||||
*/
|
||||
errorret_t mapChunkLoad(mapchunk_t* chunk);
|
||||
|
||||
/**
|
||||
* Gets the index of a chunk, within the world, at the given position.
|
||||
*
|
||||
* @param position The chunk position.
|
||||
* @return The index of the chunk, or -1 if out of bounds.
|
||||
*/
|
||||
chunkindex_t mapGetChunkIndexAt(const chunkpos_t position);
|
||||
|
||||
/**
|
||||
* Gets a chunk by its index.
|
||||
*
|
||||
* @param chunkIndex The index of the chunk.
|
||||
* @return A pointer to the chunk.
|
||||
*/
|
||||
mapchunk_t * mapGetChunk(const uint8_t chunkIndex);
|
||||
|
||||
/**
|
||||
* Gets the tile at the given world position.
|
||||
*
|
||||
* @param position The world position.
|
||||
* @return The tile at that position, or TILE_NULL if the chunk is unloaded.
|
||||
*/
|
||||
maptile_t mapGetTile(const worldpos_t position);
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "mapchunk.h"
|
||||
|
||||
uint32_t mapChunkGetTileindex(const chunkpos_t position) {
|
||||
return (
|
||||
(position.z * CHUNK_WIDTH * CHUNK_HEIGHT) +
|
||||
(position.y * CHUNK_WIDTH) +
|
||||
position.x
|
||||
);
|
||||
}
|
||||
|
||||
bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b) {
|
||||
return (a.x == b.x) && (a.y == b.y) && (a.z == b.z);
|
||||
}
|
||||
|
||||
void mapChunkRender(const mapchunk_t *chunk) {
|
||||
for(uint8_t i = 0; i < chunk->meshCount; i++) {
|
||||
meshDraw(&chunk->meshes[i], 0, -1);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "maptile.h"
|
||||
#include "worldpos.h"
|
||||
#include "display/mesh/quad.h"
|
||||
|
||||
typedef struct chunk_s {
|
||||
chunkpos_t position;
|
||||
maptile_t tiles[CHUNK_TILE_COUNT];
|
||||
|
||||
uint8_t meshCount;
|
||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
|
||||
mesh_t meshes[CHUNK_MESH_COUNT_MAX];
|
||||
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
||||
} mapchunk_t;
|
||||
|
||||
/**
|
||||
* Gets the tile index for a tile position within a chunk.
|
||||
*
|
||||
* @param position The position within the chunk.
|
||||
* @return The tile index within the chunk.
|
||||
*/
|
||||
uint32_t mapChunkGetTileindex(const chunkpos_t position);
|
||||
|
||||
/**
|
||||
* Checks if two chunk positions are equal.
|
||||
*
|
||||
* @param a The first chunk position.
|
||||
* @param b The second chunk position.
|
||||
* @return true if equal, false otherwise.
|
||||
*/
|
||||
bool_t mapChunkPositionIsEqual(const chunkpos_t a, const chunkpos_t b);
|
||||
|
||||
/**
|
||||
* Renders the given map chunk.
|
||||
*
|
||||
* @param chunk The map chunk to render.
|
||||
*/
|
||||
void mapChunkRender(const mapchunk_t *chunk);
|
||||
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "maptile.h"
|
||||
|
||||
bool_t mapTileIsWalkable(const maptile_t tile) {
|
||||
switch(tile) {
|
||||
case TILE_SHAPE_NULL:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool_t mapTileIsRamp(const maptile_t tile) {
|
||||
switch(tile) {
|
||||
case TILE_SHAPE_RAMP_NORTH:
|
||||
case TILE_SHAPE_RAMP_SOUTH:
|
||||
case TILE_SHAPE_RAMP_EAST:
|
||||
case TILE_SHAPE_RAMP_WEST:
|
||||
case TILE_SHAPE_RAMP_NORTHEAST:
|
||||
case TILE_SHAPE_RAMP_NORTHWEST:
|
||||
case TILE_SHAPE_RAMP_SOUTHEAST:
|
||||
case TILE_SHAPE_RAMP_SOUTHWEST:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "duskdefs.h"
|
||||
// #include "rpg/entity/entitydir.h"
|
||||
|
||||
typedef uint8_t maptile_t;
|
||||
|
||||
/**
|
||||
* Returns whether or not the given tile is walkable.
|
||||
*
|
||||
* @param tile The tile to check.
|
||||
* @return bool_t True if walkable, false if not.
|
||||
*/
|
||||
bool_t mapTileIsWalkable(const maptile_t tile);
|
||||
|
||||
/**
|
||||
* Returns whether or not the given tile is a ramp tile.
|
||||
*
|
||||
* @param tile The tile to check.
|
||||
* @return bool_t True if ramp, false if not.
|
||||
*/
|
||||
bool_t mapTileIsRamp(const maptile_t tile);
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "worldpos.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b) {
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out) {
|
||||
assertNotNull(chunkPos, "Chunk position pointer cannot be NULL");
|
||||
assertNotNull(out, "Output world position pointer cannot be NULL");
|
||||
|
||||
out->x = (worldunit_t)(chunkPos->x * CHUNK_WIDTH);
|
||||
out->y = (worldunit_t)(chunkPos->y * CHUNK_HEIGHT);
|
||||
out->z = (worldunit_t)(chunkPos->z * CHUNK_DEPTH);
|
||||
}
|
||||
|
||||
void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out) {
|
||||
assertNotNull(worldPos, "World position pointer cannot be NULL");
|
||||
assertNotNull(out, "Output chunk position pointer cannot be NULL");
|
||||
|
||||
if(worldPos->x < 0) {
|
||||
out->x = (chunkunit_t)((worldPos->x - (CHUNK_WIDTH - 1)) / CHUNK_WIDTH);
|
||||
} else {
|
||||
out->x = (chunkunit_t)(worldPos->x / CHUNK_WIDTH);
|
||||
}
|
||||
|
||||
if(worldPos->y < 0) {
|
||||
out->y = (chunkunit_t)((worldPos->y - (CHUNK_HEIGHT - 1)) / CHUNK_HEIGHT);
|
||||
} else {
|
||||
out->y = (chunkunit_t)(worldPos->y / CHUNK_HEIGHT);
|
||||
}
|
||||
|
||||
if(worldPos->z < 0) {
|
||||
out->z = (chunkunit_t)((worldPos->z - (CHUNK_DEPTH - 1)) / CHUNK_DEPTH);
|
||||
} else {
|
||||
out->z = (chunkunit_t)(worldPos->z / CHUNK_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos) {
|
||||
assertNotNull(worldPos, "World position pointer cannot be NULL");
|
||||
|
||||
uint8_t localX, localY, localZ;
|
||||
if(worldPos->x < 0) {
|
||||
localX = (uint8_t)(
|
||||
(CHUNK_WIDTH - 1) - ((-worldPos->x - 1) % CHUNK_WIDTH)
|
||||
);
|
||||
} else {
|
||||
localX = (uint8_t)(worldPos->x % CHUNK_WIDTH);
|
||||
}
|
||||
|
||||
if(worldPos->y < 0) {
|
||||
localY = (uint8_t)(
|
||||
(CHUNK_HEIGHT - 1) - ((-worldPos->y - 1) % CHUNK_HEIGHT)
|
||||
);
|
||||
} else {
|
||||
localY = (uint8_t)(worldPos->y % CHUNK_HEIGHT);
|
||||
}
|
||||
|
||||
if(worldPos->z < 0) {
|
||||
localZ = (uint8_t)(
|
||||
(CHUNK_DEPTH - 1) - ((-worldPos->z - 1) % CHUNK_DEPTH)
|
||||
);
|
||||
} else {
|
||||
localZ = (uint8_t)(worldPos->z % CHUNK_DEPTH);
|
||||
}
|
||||
|
||||
chunktileindex_t chunkTileIndex = (chunktileindex_t)(
|
||||
(localZ * CHUNK_WIDTH * CHUNK_HEIGHT) +
|
||||
(localY * CHUNK_WIDTH) +
|
||||
localX
|
||||
);
|
||||
assertTrue(
|
||||
chunkTileIndex < CHUNK_TILE_COUNT,
|
||||
"Calculated chunk tile index is out of bounds"
|
||||
);
|
||||
return chunkTileIndex;
|
||||
}
|
||||
|
||||
chunkindex_t chunkPosToIndex(const chunkpos_t* pos) {
|
||||
assertNotNull(pos, "Chunk position pointer cannot be NULL");
|
||||
|
||||
chunkindex_t chunkIndex = (chunkindex_t)(
|
||||
(pos->z * MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT) +
|
||||
(pos->y * MAP_CHUNK_WIDTH) +
|
||||
pos->x
|
||||
);
|
||||
|
||||
return chunkIndex;
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "duskdefs.h"
|
||||
|
||||
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH)
|
||||
|
||||
#define MAP_CHUNK_WIDTH 3
|
||||
#define MAP_CHUNK_HEIGHT 3
|
||||
#define MAP_CHUNK_DEPTH 3
|
||||
#define MAP_CHUNK_COUNT (MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT * MAP_CHUNK_DEPTH)
|
||||
|
||||
typedef int16_t worldunit_t;
|
||||
typedef int16_t chunkunit_t;
|
||||
typedef int16_t chunkindex_t;
|
||||
typedef uint32_t chunktileindex_t;
|
||||
|
||||
typedef int32_t worldunits_t;
|
||||
typedef int32_t chunkunits_t;
|
||||
|
||||
typedef struct worldpos_s {
|
||||
worldunit_t x, y, z;
|
||||
} worldpos_t;
|
||||
|
||||
typedef struct chunkpos_t {
|
||||
chunkunit_t x, y, z;
|
||||
} chunkpos_t;
|
||||
|
||||
/**
|
||||
* Compares two world positions for equality.
|
||||
*
|
||||
* @param a The first world position.
|
||||
* @param b The second world position.
|
||||
* @return true if equal, false otherwise.
|
||||
*/
|
||||
bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b);
|
||||
|
||||
/**
|
||||
* Converts a world position to a chunk position.
|
||||
*
|
||||
* @param worldPos The world position.
|
||||
* @param out The output chunk position.
|
||||
*/
|
||||
void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out);
|
||||
|
||||
/**
|
||||
* Converts a chunk position to a world position.
|
||||
*
|
||||
* @param worldPos The world position.
|
||||
* @param out The output chunk position.
|
||||
*/
|
||||
void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out);
|
||||
|
||||
/**
|
||||
* Converts a position in world-space to an index inside a chunk that the tile
|
||||
* resides in.
|
||||
*
|
||||
* @param worldPos The world position.
|
||||
* @return The tile index within the chunk.
|
||||
*/
|
||||
chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos);
|
||||
|
||||
/**
|
||||
* Converts a chunk position to a world position.
|
||||
*
|
||||
* @param worldPos The world position.
|
||||
* @param out The output chunk position.
|
||||
*/
|
||||
chunkindex_t chunkPosToIndex(const chunkpos_t* pos);
|
||||
@@ -5,5 +5,4 @@
|
||||
|
||||
# Subdirectories
|
||||
add_subdirectory(item)
|
||||
add_subdirectory(map)
|
||||
add_subdirectory(story)
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "modulemap.h"
|
||||
#include "assert/assert.h"
|
||||
#include "map/map.h"
|
||||
|
||||
void moduleMap(scriptcontext_t *ctx) {
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
// Register map functions
|
||||
lua_register(ctx->luaState, "mapIsLoaded", moduleMapIsLoaded);
|
||||
lua_register(ctx->luaState, "mapGetTile", moduleMapGetTile);
|
||||
lua_register(ctx->luaState, "mapRender", moduleMapRender);
|
||||
lua_register(ctx->luaState, "mapLoad", moduleMapLoad);
|
||||
}
|
||||
|
||||
int moduleMapIsLoaded(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL.");
|
||||
lua_pushboolean(L, mapIsLoaded());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleMapGetTile(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int moduleMapRender(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL.");
|
||||
mapRender();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int moduleMapLoad(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL.");
|
||||
|
||||
if(!lua_isstring(L, 1)) {
|
||||
luaL_error(L, "Expected string as first argument (file path).");
|
||||
return 0;
|
||||
}
|
||||
const char_t *path = lua_tostring(L, 1);
|
||||
|
||||
|
||||
// Optional position.
|
||||
chunkpos_t position = {0, 0, 0};
|
||||
|
||||
errorret_t err = mapLoad(path, position);
|
||||
if(err.code != ERROR_OK) {
|
||||
errorCatch(errorPrint(err));
|
||||
luaL_error(L, "Failed to load map!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
|
||||
/**
|
||||
* Register map functions to the given script context.
|
||||
*
|
||||
* @param context The script context to register map functions to.
|
||||
*/
|
||||
void moduleMap(scriptcontext_t *context);
|
||||
|
||||
/**
|
||||
* Script function to check if a map is loaded.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleMapIsLoaded(lua_State *L);
|
||||
|
||||
/**
|
||||
* Script function to get the tile at a given world position.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleMapGetTile(lua_State *L);
|
||||
|
||||
/**
|
||||
* Script function to render the map.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleMapRender(lua_State *L);
|
||||
|
||||
/**
|
||||
* Script function to load a map from a given file path.
|
||||
*
|
||||
* @param L The Lua state.
|
||||
* @return Number of return values on the Lua stack.
|
||||
*/
|
||||
int moduleMapLoad(lua_State *L);
|
||||
@@ -8,9 +8,7 @@
|
||||
#pragma once
|
||||
#include "script/module/item/moduleitem.h"
|
||||
#include "script/module/story/modulestoryflag.h"
|
||||
#include "script/module/map/modulemap.h"
|
||||
|
||||
#define SCRIPT_GAME_LIST \
|
||||
{ .name = "item", .callback = moduleItem }, \
|
||||
{ .name = "storyflag", .callback = moduleStoryFlag }, \
|
||||
{ .name = "map", .callback = moduleMap },
|
||||
{ .name = "storyflag", .callback = moduleStoryFlag },
|
||||
@@ -1,14 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Includes
|
||||
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(game)
|
||||
add_subdirectory(script)
|
||||
@@ -1,7 +0,0 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(module)
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
Reference in New Issue
Block a user