This commit is contained in:
2026-06-18 20:25:54 -05:00
parent 730a5b2b10
commit 57b2cdb9d1
111 changed files with 865 additions and 3328 deletions
+1 -1
View File
@@ -56,9 +56,9 @@ add_subdirectory(animation)
add_subdirectory(event)
add_subdirectory(assert)
add_subdirectory(asset)
add_subdirectory(cutscene)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(render)
add_subdirectory(log)
add_subdirectory(engine)
add_subdirectory(error)
+1 -1
View File
@@ -12,6 +12,6 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
# Subdirs
add_subdirectory(display)
# add_subdirectory(display) # disabled: pending rop-based asset loader rewrite
add_subdirectory(locale)
add_subdirectory(json)
+4 -22
View File
@@ -10,33 +10,15 @@
assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
[ASSET_LOADER_TYPE_NULL] = { 0 },
[ASSET_LOADER_TYPE_MESH] = {
.loadSync = assetMeshLoaderSync,
.loadAsync = assetMeshLoaderAsync,
.dispose = assetMeshDispose
},
[ASSET_LOADER_TYPE_TEXTURE] = {
.loadSync = assetTextureLoaderSync,
.loadAsync = assetTextureLoaderAsync,
.dispose = assetTextureDispose
},
[ASSET_LOADER_TYPE_TILESET] = {
.loadSync = assetTilesetLoaderSync,
.loadAsync = assetTilesetLoaderAsync,
.dispose = assetTilesetDispose
},
[ASSET_LOADER_TYPE_LOCALE] = {
.loadSync = assetLocaleLoaderSync,
.loadSync = assetLocaleLoaderSync,
.loadAsync = assetLocaleLoaderAsync,
.dispose = assetLocaleDispose
.dispose = assetLocaleDispose
},
[ASSET_LOADER_TYPE_JSON] = {
.loadSync = assetJsonLoaderSync,
.loadSync = assetJsonLoaderSync,
.loadAsync = assetJsonLoaderAsync,
.dispose = assetJsonDispose
.dispose = assetJsonDispose
},
};
+3 -20
View File
@@ -6,46 +6,29 @@
*/
#pragma once
#include "asset/loader/display/assetmeshloader.h"
#include "asset/loader/display/assettextureloader.h"
#include "asset/loader/display/assettilesetloader.h"
#include "asset/loader/locale/assetlocaleloader.h"
#include "asset/loader/json/assetjsonloader.h"
typedef enum {
ASSET_LOADER_TYPE_NULL,
ASSET_LOADER_TYPE_MESH,
ASSET_LOADER_TYPE_TEXTURE,
ASSET_LOADER_TYPE_TILESET,
ASSET_LOADER_TYPE_LOCALE,
ASSET_LOADER_TYPE_JSON,
ASSET_LOADER_TYPE_COUNT
} assetloadertype_t;
typedef union {
assetmeshloaderinput_t mesh;
assettextureloaderinput_t texture;
assettilesetloaderinput_t tileset;
assetlocaleloaderinput_t locale;
assetjsonloaderinput_t json;
assetjsonloaderinput_t json;
} assetloaderinput_t;
typedef union {
assetmeshloaderloading_t mesh;
assettextureloaderloading_t texture;
assettilesetloaderloading_t tileset;
assetlocaleloaderloading_t locale;
assetjsonloaderloading_t json;
assetjsonloaderloading_t json;
} assetloaderloading_t;
typedef union {
assetmeshoutput_t mesh;
assettextureoutput_t texture;
assettilesetoutput_t tileset;
assetlocaleoutput_t locale;
assetjsonoutput_t json;
assetjsonoutput_t json;
} assetloaderoutput_t;
typedef struct assetloading_s assetloading_t;
+17 -30
View File
@@ -12,9 +12,6 @@
#include "input/input.h"
#include "log/log.h"
#include "engine/engine.h"
#include "display/shader/shaderunlit.h"
#include "display/text/text.h"
#include "display/spritebatch/spritebatch.h"
console_t CONSOLE;
@@ -22,9 +19,9 @@ void consoleInit(void) {
memoryZero(&CONSOLE, sizeof(console_t));
CONSOLE.visible = true;
#ifdef DUSK_CONSOLE_POSIX
threadMutexInit(&CONSOLE.printMutex);
#endif
#ifdef DUSK_CONSOLE_POSIX
threadMutexInit(&CONSOLE.printMutex);
#endif
}
void consolePrint(const char_t *message, ...) {
@@ -35,9 +32,9 @@ void consolePrint(const char_t *message, ...) {
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
va_end(args);
#ifdef DUSK_CONSOLE_POSIX
threadMutexLock(&CONSOLE.printMutex);
#endif
#ifdef DUSK_CONSOLE_POSIX
threadMutexLock(&CONSOLE.printMutex);
#endif
memoryMove(
CONSOLE.line[0],
@@ -46,17 +43,17 @@ void consolePrint(const char_t *message, ...) {
);
memoryCopy(CONSOLE.line[CONSOLE_HISTORY_MAX - 1], buffer, len + 1);
#ifdef DUSK_CONSOLE_POSIX
threadMutexUnlock(&CONSOLE.printMutex);
#endif
#ifdef DUSK_CONSOLE_POSIX
threadMutexUnlock(&CONSOLE.printMutex);
#endif
logDebug("%s\n", buffer);
}
void consoleUpdate(void) {
#ifdef DUSK_TIME_DYNAMIC
if(TIME.dynamicUpdate) return;
#endif
#ifdef DUSK_TIME_DYNAMIC
if(TIME.dynamicUpdate) return;
#endif
if(inputPressed(INPUT_ACTION_CONSOLE)) {
CONSOLE.visible = !CONSOLE.visible;
@@ -64,21 +61,11 @@ void consoleUpdate(void) {
}
errorret_t consoleDraw(void) {
if(!CONSOLE.visible) errorOk();
for(uint32_t i = 0; i < CONSOLE_HISTORY_MAX; i++) {
errorChain(textDraw(
0, FONT_DEFAULT.tileset->tileHeight * i,
CONSOLE.line[i],
COLOR_RED,
&FONT_DEFAULT
));
}
return spriteBatchFlush();
errorOk();
}
void consoleDispose(void) {
#ifdef DUSK_CONSOLE_POSIX
threadMutexDispose(&CONSOLE.printMutex);
#endif
}
#ifdef DUSK_CONSOLE_POSIX
threadMutexDispose(&CONSOLE.printMutex);
#endif
}
-96
View File
@@ -1,96 +0,0 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "cutscene.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "console/console.h"
#include "time/time.h"
cutscene_t CUTSCENE;
errorret_t cutsceneInit(void) {
memoryZero(&CUTSCENE, sizeof(cutscene_t));
errorOk();
}
errorret_t cutsceneUpdate(void) {
#ifdef DUSK_TIME_DYNAMIC
if(TIME.dynamicUpdate) {
errorOk();
}
#endif
if(!CUTSCENE.active) errorOk();
cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent];
if(event->onUpdate) errorChain(event->onUpdate());
errorOk();
}
errorret_t cutscenePlay(
const cutsceneevent_t *events,
const uint8_t eventCount
) {
assertNotNull(events, "Events cannot be null");
assertTrue(eventCount > 0, "Event count must be greater than zero");
assertTrue(
eventCount <= CUTSCENE_EVENT_COUNT_MAX,
"Event count exceeds CUTSCENE_EVENT_COUNT_MAX"
);
if(CUTSCENE.active) {
errorChain(cutsceneStop());
}
memoryCopy(CUTSCENE.events, events, sizeof(cutsceneevent_t) * eventCount);
CUTSCENE.eventCount = eventCount;
CUTSCENE.eventCurrent = 0;
CUTSCENE.active = true;
cutsceneevent_t *firstEvent = &CUTSCENE.events[0];
if(firstEvent->onStart) errorChain(firstEvent->onStart());
errorOk();
}
errorret_t cutsceneAdvance(void) {
if(!CUTSCENE.active) errorOk();
cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
if(currentEvent->onEnd) errorChain(currentEvent->onEnd());
CUTSCENE.eventCurrent++;
if(CUTSCENE.eventCurrent >= CUTSCENE.eventCount) {
if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop());
CUTSCENE.active = false;
errorOk();
}
cutsceneevent_t *nextEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
if(nextEvent->onStart) errorChain(nextEvent->onStart());
consolePrint("Cutscene advance");
errorOk();
}
errorret_t cutsceneStop(void) {
if(!CUTSCENE.active) errorOk();
cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
if(currentEvent->onEnd) errorChain(currentEvent->onEnd());
if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop());
CUTSCENE.active = false;
errorOk();
}
errorret_t cutsceneDispose(void) {
errorChain(cutsceneStop());
errorOk();
}
bool_t cutsceneIsActive(void) {
return CUTSCENE.active;
}
-85
View File
@@ -1,85 +0,0 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "error/error.h"
#define CUTSCENE_EVENT_COUNT_MAX 16
typedef struct {
errorret_t (*onStart)(void);
errorret_t (*onEnd)(void);
errorret_t (*onUpdate)(void);
} cutsceneevent_t;
typedef struct {
cutsceneevent_t events[CUTSCENE_EVENT_COUNT_MAX];
uint8_t eventCount;
uint8_t eventCurrent;
errorret_t (*onStop)(void);
bool_t active;
} cutscene_t;
extern cutscene_t CUTSCENE;
/**
* Initializes the cutscene manager.
*
* @return Any error state that happened.
*/
errorret_t cutsceneInit(void);
/**
* Ticks the active cutscene event, calling its onUpdate callback.
* Does nothing when no cutscene is playing.
*
* @return Any error state that happened.
*/
errorret_t cutsceneUpdate(void);
/**
* Copies the given event array and begins playing from the first
* event. If a cutscene is already playing it is stopped first.
*
* @param events Array of events to copy.
* @param eventCount Number of events. Must be > 0 and
* <= CUTSCENE_EVENT_COUNT_MAX.
* @return Any error state that happened.
*/
errorret_t cutscenePlay(
const cutsceneevent_t *events,
const uint8_t eventCount
);
/**
* Ends the current event and starts the next one.
* Marks the cutscene as inactive after the last event ends.
* Does nothing when no cutscene is playing.
*
* @return Any error state that happened.
*/
errorret_t cutsceneAdvance(void);
/**
* Ends the current event and stops the cutscene immediately.
* Does nothing when no cutscene is playing.
*
* @return Any error state that happened.
*/
errorret_t cutsceneStop(void);
/**
* Disposes of the cutscene manager, stopping any active cutscene.
*
* @return Any error state that happened.
*/
errorret_t cutsceneDispose(void);
/**
* Returns whether a cutscene is currently playing.
*
* @return true if a cutscene is active.
*/
bool_t cutsceneIsActive(void);
+2 -13
View File
@@ -1,28 +1,17 @@
# Copyright (c) 2025 Dominic Masters
# 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
display.c
)
# Subdirectories
add_subdirectory(framebuffer)
add_subdirectory(mesh)
add_subdirectory(screen)
add_subdirectory(shader)
add_subdirectory(spritebatch)
add_subdirectory(text)
add_subdirectory(texture)
# Color definitions
dusk_run_python(
dusk_color_defs
tools.color.csv
--csv ${CMAKE_CURRENT_SOURCE_DIR}/color.csv
--output ${DUSK_GENERATED_HEADERS_DIR}/display/color.h
)
add_dependencies(${DUSK_LIBRARY_TARGET_NAME} dusk_color_defs)
add_dependencies(${DUSK_LIBRARY_TARGET_NAME} dusk_color_defs)
+16 -92
View File
@@ -1,116 +1,40 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/display.h"
#include "display/framebuffer/framebuffer.h"
#include "render/ropbuffer.h"
#include "scene/scene.h"
#include "display/spritebatch/spritebatch.h"
#include "display/mesh/quad.h"
#include "display/mesh/cube.h"
#include "display/mesh/sphere.h"
#include "display/mesh/plane.h"
#include "display/mesh/capsule.h"
#include "display/mesh/triprism.h"
#include "display/screen/screen.h"
#include "ui/ui.h"
#include "display/text/text.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "asset/asset.h"
#include "display/shader/shaderlist.h"
#include "time/time.h"
display_t DISPLAY = { 0 };
errorret_t displayInit(void) {
memoryZero(&DISPLAY, sizeof(DISPLAY));
#ifdef displayPlatformInit
errorChain(displayPlatformInit());
#endif
errorChain(displaySetState((displaystate_t){ .flags = 0 }));
errorChain(textureInit(
&TEXTURE_WHITE, 4, 4,
TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_WHITE_PIXELS }
));
errorChain(textureInit(
&TEXTURE_TEST, 4, 4,
TEXTURE_FORMAT_RGBA, (texturedata_t){ .rgbaColors = TEXTURE_TEST_PIXELS }
));
// Standard meshes
errorChain(quadInit());
errorChain(cubeInit());
errorChain(sphereInit());
errorChain(planeInit());
errorChain(capsuleInit());
errorChain(triPrismInit());
errorChain(frameBufferInitBackBuffer());
errorChain(spriteBatchInit());
errorChain(textInit());
errorChain(screenInit());
// Setup initial shader with default values
errorChain(shaderListInit());
#ifdef displayPlatformInit
errorChain(displayPlatformInit());
#endif
errorOk();
}
errorret_t displayUpdate(void) {
#ifdef displayPlatformUpdate
errorChain(displayPlatformUpdate());
#endif
// Reset state
spriteBatchClear();
errorChain(frameBufferBind(NULL));
// Bind screen and render scene
errorChain(screenBind());
frameBufferClear(
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
SCREEN.background
);
ropBufferReset(&ROPBUFFER);
errorChain(sceneRender());
// Finish up
screenUnbind();
screenRender();
// Swap and return.
#ifdef displayPlatformSwap
errorChain(displayPlatformSwap());
#endif
errorOk();
}
errorret_t displaySetState(displaystate_t state) {
#ifdef displayPlatformSetState
errorChain(displayPlatformSetState(state));
#endif
#ifdef displayPlatformFlush
errorChain(displayPlatformFlush(&ROPBUFFER));
#endif
#ifdef displayPlatformSwap
errorChain(displayPlatformSwap());
#endif
errorOk();
}
errorret_t displayDispose(void) {
errorChain(shaderListDispose());
errorChain(spriteBatchDispose());
screenDispose();
errorChain(textDispose());
errorChain(textureDispose(&TEXTURE_WHITE));
errorChain(textureDispose(&TEXTURE_TEST));
#ifdef displayPlatformDispose
displayPlatformDispose();
#endif
// For now, we just return an OK error.
#ifdef displayPlatformDispose
displayPlatformDispose();
#endif
errorOk();
}
}
+3 -50
View File
@@ -1,65 +1,18 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/displayplatform.h"
#include "display/displaystate.h"
// Expecting some definitions to be provided
#ifndef DUSK_DISPLAY_SIZE_DYNAMIC
#ifndef DUSK_DISPLAY_WIDTH
#error "DUSK_DISPLAY_WIDTH must be defined."
#endif
#ifndef DUSK_DISPLAY_HEIGHT
#error "DUSK_DISPLAY_HEIGHT must be defined"
#endif
#define DUSK_DISPLAY_WIDTH_DEFAULT DUSK_DISPLAY_WIDTH
#define DUSK_DISPLAY_HEIGHT_DEFAULT DUSK_DISPLAY_HEIGHT
#else
#ifndef DUSK_DISPLAY_WIDTH_DEFAULT
#error "DUSK_DISPLAY_WIDTH_DEFAULT must be defined."
#endif
#ifndef DUSK_DISPLAY_HEIGHT_DEFAULT
#error "DUSK_DISPLAY_HEIGHT_DEFAULT must be defined."
#endif
#ifdef DUSK_DISPLAY_WIDTH
#error "DUSK_DISPLAY_WIDTH should not be defined."
#endif
#ifdef DUSK_DISPLAY_HEIGHT
#error "DUSK_DISPLAY_HEIGHT should not be defined."
#endif
#endif
// Main Display Struct, platform-speicifc
typedef displayplatform_t display_t;
extern display_t DISPLAY;
/**
* Initializes the display system.
* @return An errorret_t indicating success or failure.
*/
errorret_t displayInit(void);
/**
* Tells the display system to actually draw the frame.
* @return An errorret_t indicating success or failure.
*/
errorret_t displayUpdate(void);
/**
* Sets the display state.
*
* @param state The state to set.
* @return An errorret_t indicating success or failure.
*/
errorret_t displaySetState(displaystate_t state);
/**
* Disposes of the display system.
* @return An errorret_t indicating success or failure.
*/
errorret_t displayDispose(void);
errorret_t displayDispose(void);
+4 -4
View File
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
@@ -8,10 +8,10 @@
#pragma once
#include "dusk.h"
#define DISPLAY_STATE_FLAG_CULL (1 << 0)
#define DISPLAY_STATE_FLAG_CULL (1 << 0)
#define DISPLAY_STATE_FLAG_DEPTH_TEST (1 << 1)
#define DISPLAY_STATE_FLAG_BLEND (1 << 2)
#define DISPLAY_STATE_FLAG_BLEND (1 << 2)
typedef struct {
uint8_t flags;
} displaystate_t;
} displaystate_t;
@@ -1,10 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
framebuffer.c
)
@@ -1,77 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "framebuffer.h"
#include "display/display.h"
#include "assert/assert.h"
#include "util/memory.h"
framebuffer_t FRAMEBUFFER_BACKBUFFER = {0};
const framebuffer_t *FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
errorret_t frameBufferInitBackBuffer() {
memoryZero(&FRAMEBUFFER_BACKBUFFER, sizeof(framebuffer_t));
errorChain(frameBufferPlatformInitBackBuffer());
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
errorOk();
}
errorret_t frameBufferBind(framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
frameBufferBind(&FRAMEBUFFER_BACKBUFFER);
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
errorOk();
}
errorChain(frameBufferPlatformBind(framebuffer));
FRAMEBUFFER_BOUND = framebuffer;
errorOk();
}
uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer) {
return frameBufferPlatformGetWidth(framebuffer);
}
uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer) {
return frameBufferPlatformGetHeight(framebuffer);
}
float_t frameBufferGetAspect(const framebuffer_t *framebuffer) {
#ifdef frameBufferPlatformGetAspect
return frameBufferPlatformGetAspect(framebuffer);
#endif
uint32_t width = frameBufferGetWidth(framebuffer);
uint32_t height = frameBufferGetHeight(framebuffer);
if(height == 0) return 1.0f; // Avoid divide by zero, just return 1:1 aspect.
return (float_t)width / (float_t)height;
}
void frameBufferClear(const uint8_t flags, const color_t color) {
frameBufferPlatformClear(flags, color);
}
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
errorret_t frameBufferInit(
framebuffer_t *fb,
const uint32_t width,
const uint32_t height
) {
assertNotNull(fb, "Framebuffer cannot be NULL");
assertTrue(width > 0 && height > 0, "W/H must be greater than 0");
memoryZero(fb, sizeof(framebuffer_t));
errorChain(frameBufferPlatformInit(fb, width, height));
errorOk();
}
errorret_t frameBufferDispose(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
errorChain(frameBufferPlatformDispose(framebuffer));
errorOk();
}
#endif
-118
View File
@@ -1,118 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/framebuffer/framebufferplatform.h"
#include "display/texture/texture.h"
// Expected defs.
#ifndef frameBufferPlatformInitBackBuffer
#error "frameBufferPlatformInitBackBuffer not defined for this platform"
#endif
#ifndef frameBufferPlatformBind
#error "frameBufferPlatformBind not defined for this platform"
#endif
#ifndef frameBufferPlatformGetWidth
#error "frameBufferPlatformGetWidth not defined for this platform"
#endif
#ifndef frameBufferPlatformGetHeight
#error "frameBufferPlatformGetHeight not defined for this platform"
#endif
#ifndef frameBufferPlatformClear
#error "frameBufferPlatformClear not defined for this platform"
#endif
#define FRAMEBUFFER_CLEAR_COLOR (1 << 0)
#define FRAMEBUFFER_CLEAR_DEPTH (1 << 1)
typedef framebufferplatform_t framebuffer_t;
extern framebuffer_t FRAMEBUFFER_BACKBUFFER;
extern const framebuffer_t *FRAMEBUFFER_BOUND;
/**
* Initializes the backbuffer framebuffer.
*
* @return Error for initialization of the backbuffer.
*/
errorret_t frameBufferInitBackBuffer(void);
/**
* Gets the width of the framebuffer.
*
* @param framebuffer The framebuffer to get the width of.
* @return The width of the framebuffer, or 0 if the framebuffer is NULL.
*/
uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer);
/**
* Gets the height of the framebuffer.
*
* @param framebuffer The framebuffer to get the height of.
* @return The height of the framebuffer, or 0 if the framebuffer is NULL.
*/
uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer);
/**
* Returns the aspect ratio of the framebuffer. This is ALMOST always just
* the width / height, however some platforms may choose to override this if
* they have stretched styled back buffers, e.g. 640x480 stretched.
*
* @param framebuffer The framebuffer to get the aspect ratio of.
* @return The aspect ratio of the framebuffer.
*/
float_t frameBufferGetAspect(const framebuffer_t *framebuffer);
/**
* Binds the framebuffer for rendering, or the backbuffer if the framebuffer
* provided is NULL.
*
* @param framebuffer The framebuffer to bind, or NULL to bind the backbuffer.
* @return Error for binding the framebuffer.
*/
errorret_t frameBufferBind(framebuffer_t *framebuffer);
/**
* Clears the currently bound framebuffer.
*
* @param flags The clear flags.
* @param color The color to clear the color buffer to (if clearing color).
*/
void frameBufferClear(uint8_t flags, color_t color);
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
#ifndef frameBufferPlatformInit
#error "frameBufferPlatformInit not defined for this platform"
#endif
#ifndef frameBufferPlatformDispose
#error "frameBufferPlatformDispose not defined for this platform"
#endif
/**
* Initializes a framebuffer.
*
* @param framebuffer The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
* @return Error for initialization of the framebuffer.
*/
errorret_t frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
);
/**
* Disposes of the framebuffer. Will also be used for request disposing of the
* backbuffer.
*
* @param framebuffer The framebuffer to dispose of.
*/
errorret_t frameBufferDispose(framebuffer_t *framebuffer);
#endif
-16
View File
@@ -1,16 +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
mesh.c
quad.c
cube.c
sphere.c
plane.c
capsule.c
triprism.c
)
-192
View File
@@ -1,192 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "capsule.h"
#include "assert/assert.h"
mesh_t CAPSULE_MESH_SIMPLE;
meshvertex_t CAPSULE_MESH_SIMPLE_VERTICES[CAPSULE_VERTEX_COUNT];
errorret_t capsuleInit() {
vec3 center = { 0.0f, 0.0f, 0.0f };
capsuleBuffer(
CAPSULE_MESH_SIMPLE_VERTICES,
center,
0.5f,
0.5f,
CAPSULE_CAP_RINGS,
CAPSULE_SECTORS
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&CAPSULE_MESH_SIMPLE,
CAPSULE_PRIMITIVE_TYPE,
CAPSULE_VERTEX_COUNT,
CAPSULE_MESH_SIMPLE_VERTICES
));
errorOk();
}
void capsuleBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const float_t halfHeight,
const int32_t capRings,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(center, "Center vector cannot be NULL");
const float_t cx = center[0];
const float_t cy = center[1];
const float_t cz = center[2];
const float_t sectorStep = 2.0f * (float_t)GLM_PI / (float_t)sectors;
int32_t vi = 0;
/* Helper macro: write one vertex. */
#if MESH_ENABLE_COLOR
#define CAP_VERT(px, py, pz, u, v) \
vertices[vi].color = color; \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#else
#define CAP_VERT(px, py, pz, u, v) \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#endif
/* ---- Top hemisphere ---- */
/* phi ranges from PI/2 (top pole) down to 0 (equator). */
const float_t capStep = (float_t)GLM_PI_2 / (float_t)capRings;
for(int32_t i = 0; i < capRings; i++) {
const float_t phi1 = (float_t)GLM_PI_2 - (float_t)i * capStep;
const float_t phi2 = (float_t)GLM_PI_2 - (float_t)(i + 1) * capStep;
const float_t ly1 = radius * sinf(phi1);
const float_t ly2 = radius * sinf(phi2);
const float_t lxz1 = radius * cosf(phi1);
const float_t lxz2 = radius * cosf(phi2);
/* UV: top cap occupies v in [0.5 + halfHeightFrac .. 1.0]: we use a
* simple per-band normalisation against the full height. */
const float_t v1 = 1.0f - (float_t)i / (float_t)(2 * capRings + 1);
const float_t v2 = 1.0f - (float_t)(i + 1) / (float_t)(2 * capRings + 1);
for(int32_t j = 0; j < sectors; j++) {
const float_t t1 = (float_t)j * sectorStep;
const float_t t2 = (float_t)(j + 1) * sectorStep;
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
const float_t x11 = lxz1 * cosf(t1), z11 = lxz1 * sinf(t1);
const float_t x12 = lxz1 * cosf(t2), z12 = lxz1 * sinf(t2);
const float_t x21 = lxz2 * cosf(t1), z21 = lxz2 * sinf(t1);
const float_t x22 = lxz2 * cosf(t2), z22 = lxz2 * sinf(t2);
const float_t y1off = cy + halfHeight + ly1;
const float_t y2off = cy + halfHeight + ly2;
CAP_VERT(cx+x11, y1off, cz+z11, u1, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x22, y2off, cz+z22, u2, v2)
}
}
/* ---- Cylindrical body ---- */
{
const float_t yTop = cy + halfHeight;
const float_t yBot = cy - halfHeight;
const float_t vTop = (
1.0f - (float_t)capRings / (float_t)(2 * capRings + 1)
);
const float_t vBot = (
1.0f - (float_t)(capRings + 1) / (float_t)(2 * capRings + 1)
);
for(int32_t j = 0; j < sectors; j++) {
const float_t t1 = (float_t)j * sectorStep;
const float_t t2 = (float_t)(j + 1) * sectorStep;
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
const float_t x1 = radius * cosf(t1), z1 = radius * sinf(t1);
const float_t x2 = radius * cosf(t2), z2 = radius * sinf(t2);
CAP_VERT(cx+x1, yTop, cz+z1, u1, vTop)
CAP_VERT(cx+x1, yBot, cz+z1, u1, vBot)
CAP_VERT(cx+x2, yTop, cz+z2, u2, vTop)
CAP_VERT(cx+x2, yTop, cz+z2, u2, vTop)
CAP_VERT(cx+x1, yBot, cz+z1, u1, vBot)
CAP_VERT(cx+x2, yBot, cz+z2, u2, vBot)
}
}
// Bottom hemisphere
for(int32_t i = 0; i < capRings; i++) {
const float_t phi1 = -(float_t)i * capStep;
const float_t phi2 = -(float_t)(i + 1) * capStep;
const float_t ly1 = radius * sinf(phi1);
const float_t ly2 = radius * sinf(phi2);
const float_t lxz1 = radius * cosf(phi1);
const float_t lxz2 = radius * cosf(phi2);
const float_t v1 = (
1.0f - (float_t)(capRings + 1 + i) / (float_t)(2 * capRings + 1)
);
const float_t v2 = (
1.0f - (float_t)(capRings + 1 + i + 1) / (float_t)(2 * capRings + 1)
);
for(int32_t j = 0; j < sectors; j++) {
const float_t t1 = (float_t)j * sectorStep;
const float_t t2 = (float_t)(j + 1) * sectorStep;
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
const float_t x11 = lxz1 * cosf(t1), z11 = lxz1 * sinf(t1);
const float_t x12 = lxz1 * cosf(t2), z12 = lxz1 * sinf(t2);
const float_t x21 = lxz2 * cosf(t1), z21 = lxz2 * sinf(t1);
const float_t x22 = lxz2 * cosf(t2), z22 = lxz2 * sinf(t2);
const float_t y1off = cy - halfHeight + ly1;
const float_t y2off = cy - halfHeight + ly2;
CAP_VERT(cx+x11, y1off, cz+z11, u1, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x12, y1off, cz+z12, u2, v1)
CAP_VERT(cx+x21, y2off, cz+z21, u1, v2)
CAP_VERT(cx+x22, y2off, cz+z22, u2, v2)
}
}
#undef CAP_VERT
}
-52
View File
@@ -1,52 +0,0 @@
/**
* 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 CAPSULE_CAP_RINGS 4
#define CAPSULE_SECTORS 16
#define CAPSULE_VERTEX_COUNT ((2 * CAPSULE_CAP_RINGS + 1) * CAPSULE_SECTORS * 6)
#define CAPSULE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t CAPSULE_MESH_SIMPLE;
extern meshvertex_t CAPSULE_MESH_SIMPLE_VERTICES[CAPSULE_VERTEX_COUNT];
/**
* Initializes the simple unit capsule mesh centered at (0,0,0) with radius 0.5
* and a cylindrical half-height of 0.5 (total height 2.0).
*
* @return Error for initialization of the capsule mesh.
*/
errorret_t capsuleInit();
/**
* Buffers a capsule (cylinder + two hemisphere caps) into the provided vertex
* array. The capsule's long axis is Y. Total vertex count is
* (2*capRings + 1) * sectors * 6.
*
* @param vertices Vertex array to write into.
* @param center Center position of the capsule.
* @param radius Radius of the cylinder and hemisphere caps.
* @param halfHeight Half the height of the cylindrical section only (caps
* extend an additional radius above/below).
* @param capRings Number of latitude rings per hemisphere cap.
* @param sectors Number of longitude segments around the circumference.
* @param color Color applied to all vertices.
*/
void capsuleBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const float_t halfHeight,
const int32_t capRings,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
-114
View File
@@ -1,114 +0,0 @@
/**
* 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
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
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.
#if MESH_ENABLE_COLOR
#define CUBE_VERT(i, px, py, pz, u, v) \
vertices[i].color = color; \
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);
#else
#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);
#endif
void cubeBuffer(
meshvertex_t *vertices,
const vec3 min,
const vec3 max
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
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
}
-43
View File
@@ -1,43 +0,0 @@
/**
* 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.
* @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
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
-116
View File
@@ -1,116 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mesh.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "util/math.h"
errorret_t meshInit(
mesh_t *mesh,
const meshprimitivetype_t primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertNotNull(vertices, "Vertices cannot be NULL");
assertTrue(vertexCount > 0, "Vertex count must be greater than 0");
memoryZero(mesh, sizeof(mesh_t));
errorChain(meshInitPlatform(mesh, primitiveType, vertexCount, vertices));
errorOk();
}
errorret_t meshFlush(
mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
) {
#ifdef meshFlushPlatform
assertNotNull(mesh, "Mesh cannot be NULL");
assertTrue(vertexOffset >= 0, "Vertex offset must be non-negative.");
assertTrue(
vertexCount == -1 || vertexCount > 0, "Vertex count incorrect."
);
int32_t vertCount = meshGetVertexCount(mesh);
assertTrue(
vertexOffset < (vertCount - 1), "Need at least one vert to draw"
);
int32_t drawCount = vertexCount;
if(vertexCount == -1) {
drawCount = vertCount - vertexOffset;
}
errorChain(meshFlushPlatform(mesh, vertexOffset, vertexCount));
#endif
errorOk();
}
errorret_t meshDraw(
const mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertTrue(vertexOffset >= 0, "Vertex offset must be non-negative");
assertTrue(vertexCount == -1 || vertexCount > 0, "Incorrect vert count");
int32_t vertDrawCount = vertexCount;
if(vertexCount == -1) {
const int32_t totalVertices = meshGetVertexCount(mesh);
vertDrawCount = totalVertices - vertexOffset;
}
if(vertDrawCount == 0) {
errorOk();
}
assertTrue(
vertexOffset + vertDrawCount <= meshGetVertexCount(mesh),
"Vertex offset and count must be within vertex count bounds"
);
errorChain(meshDrawPlatform(mesh, vertexOffset, vertDrawCount));
errorOk();
}
void meshGetBounds(
const mesh_t *mesh,
vec3 outMin,
vec3 outMax
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertNotNull(outMin, "Output min cannot be NULL");
assertNotNull(outMax, "Output max cannot be NULL");
for(int i = 0; i < 3; i++) {
outMin[i] = FLT_MAX;
outMax[i] = -FLT_MAX;
}
for(uint32_t i = 0; i < mesh->vertexCount; i++) {
meshvertex_t vert = mesh->vertices[i];
for(int j = 0; j < 3; j++) {
outMin[j] = mathMin(outMin[j], vert.pos[j]);
outMax[j] = mathMax(outMax[j], vert.pos[j]);
}
}
}
int32_t meshGetVertexCount(const mesh_t *mesh) {
assertNotNull(mesh, "Mesh cannot be NULL");
return meshGetVertexCountPlatform(mesh);
}
errorret_t meshDispose(mesh_t *mesh) {
assertNotNull(mesh, "Mesh cannot be NULL");
errorChain(meshDisposePlatform(mesh));
memoryZero(mesh, sizeof(mesh_t));
errorOk();
}
-100
View File
@@ -1,100 +0,0 @@
// Copyright (c) 2026 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/display.h"
#include "display/color.h"
#include "display/mesh/meshplatform.h"
#ifndef meshInitPlatform
#error "meshInitPlatform must be defined"
#endif
#ifndef meshDrawPlatform
#error "meshDrawPlatform must be defined"
#endif
#ifndef meshGetVertexCountPlatform
#error "meshGetVertexCountPlatform must be defined"
#endif
#ifndef meshDisposePlatform
#error "meshDisposePlatform must be defined"
#endif
typedef meshprimitivetypeplatform_t meshprimitivetype_t;
typedef meshplatform_t mesh_t;
/**
* Initializes a mesh.
*
* @param mesh The mesh to initialize.
* @param primitiveType The OpenGL primitive type (e.g., GL_TRIANGLES).
* @param vertexCount The number of vertices in the mesh.
* @param vertices The vertex data for the mesh.
* @return An error indicating success or failure.
*/
errorret_t meshInit(
mesh_t *mesh,
const meshprimitivetype_t primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
);
/**
* Instructs the mesh to flush the vertices to the GPU. This is surprisingly
* only really necessary on modern devices, as we tend to let older devices
* read the vertices from the main memory directly.
*
* @param mesh Mesh to flush the vertices for.
* @param vertexOffset Start vertex to flush.
* @param vertexCount Count of vertices to flush, set to -1 for all.
* @return Error state.
*/
errorret_t meshFlush(
mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
);
/**
* Draws a mesh.
*
* @param mesh The mesh to draw.
* @param vertexOffset The offset in the vertex array to start drawing from.
* @param vertexCount The number of vertices to draw. If -1, draws all vertices.
* @return An error indicating success or failure.
*/
errorret_t meshDraw(
const mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
);
/**
* Gets the axis-aligned bounding box of a mesh.
*
* @param mesh The mesh to get the bounds of.
* @param outMin Output parameter for the minimum corner of the bounding box.
* @param outMax Output parameter for the maximum corner of the bounding box.
*/
void meshGetBounds(
const mesh_t *mesh,
vec3 outMin,
vec3 outMax
);
/**
* Gets the vertex count of a mesh.
*
* @param mesh The mesh to get the vertex count from.
* @return The vertex count of the mesh.
*/
int32_t meshGetVertexCount(const mesh_t *mesh);
/**
* Disposes a mesh.
*
* @param mesh The mesh to dispose.
* @return An error indicating success or failure.
*/
errorret_t meshDispose(mesh_t *mesh);
-26
View File
@@ -1,26 +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"
#include "display/color.h"
#ifndef MESH_ENABLE_COLOR
#define MESH_ENABLE_COLOR 0
#endif
#define MESH_VERTEX_UV_SIZE 2
#define MESH_VERTEX_POS_SIZE 3
typedef struct {
#if MESH_ENABLE_COLOR
color_t color;
#endif
float_t uv[MESH_VERTEX_UV_SIZE];
float_t pos[MESH_VERTEX_POS_SIZE];
} meshvertex_t;
-116
View File
@@ -1,116 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "plane.h"
#include "assert/assert.h"
mesh_t PLANE_MESH_SIMPLE;
meshvertex_t PLANE_MESH_SIMPLE_VERTICES[PLANE_VERTEX_COUNT];
errorret_t planeInit() {
vec3 min = { 0.0f, 0.0f, 0.0f };
vec3 max = { 1.0f, 0.0f, 1.0f };
vec2 uvMin = { 0.0f, 0.0f };
vec2 uvMax = { 1.0f, 1.0f };
planeBuffer(
PLANE_MESH_SIMPLE_VERTICES,
PLANE_AXIS_XZ,
min,
max
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
, uvMin,
uvMax
);
errorChain(meshInit(
&PLANE_MESH_SIMPLE,
PLANE_PRIMITIVE_TYPE,
PLANE_VERTEX_COUNT,
PLANE_MESH_SIMPLE_VERTICES
));
errorOk();
}
/* Helper macro to write one vertex. */
#if MESH_ENABLE_COLOR
#define PLANE_VERT(i, px, py, pz, u, v) \
vertices[i].color = color; \
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);
#else
#define PLANE_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);
#endif
void planeBuffer(
meshvertex_t *vertices,
const planeaxis_t axis,
const vec3 min,
const vec3 max
#if MESH_ENABLE_COLOR
, const color_t color
#endif
, const vec2 uvMin,
const vec2 uvMax
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(min, "Min vector cannot be NULL");
assertNotNull(max, "Max vector cannot be NULL");
assertNotNull(uvMin, "uvMin cannot be NULL");
assertNotNull(uvMax, "uvMax cannot be NULL");
const float_t u0 = uvMin[0], u1 = uvMax[0];
const float_t v0 = uvMin[1], v1 = uvMax[1];
switch(axis) {
case PLANE_AXIS_XY: {
/* Flat in XY at z = min[2]; spans X and Y. */
const float_t z = min[2];
PLANE_VERT(0, min[0], min[1], z, u0, v0)
PLANE_VERT(1, max[0], min[1], z, u1, v0)
PLANE_VERT(2, max[0], max[1], z, u1, v1)
PLANE_VERT(3, min[0], min[1], z, u0, v0)
PLANE_VERT(4, max[0], max[1], z, u1, v1)
PLANE_VERT(5, min[0], max[1], z, u0, v1)
break;
}
case PLANE_AXIS_XZ: {
/* Flat in XZ at y = min[1]; spans X and Z. */
const float_t y = min[1];
PLANE_VERT(0, min[0], y, min[2], u0, v0)
PLANE_VERT(1, max[0], y, min[2], u1, v0)
PLANE_VERT(2, max[0], y, max[2], u1, v1)
PLANE_VERT(3, min[0], y, min[2], u0, v0)
PLANE_VERT(4, max[0], y, max[2], u1, v1)
PLANE_VERT(5, min[0], y, max[2], u0, v1)
break;
}
case PLANE_AXIS_YZ: {
/* Flat in YZ at x = min[0]; spans Y and Z. */
const float_t x = min[0];
PLANE_VERT(0, x, min[1], min[2], u0, v0)
PLANE_VERT(1, x, max[1], min[2], u1, v0)
PLANE_VERT(2, x, max[1], max[2], u1, v1)
PLANE_VERT(3, x, min[1], min[2], u0, v0)
PLANE_VERT(4, x, max[1], max[2], u1, v1)
PLANE_VERT(5, x, min[1], max[2], u0, v1)
break;
}
}
#undef PLANE_VERT
}
-61
View File
@@ -1,61 +0,0 @@
/**
* 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 PLANE_VERTEX_COUNT 6
#define PLANE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
/** Which axis the plane's normal points along. */
typedef enum {
PLANE_AXIS_XY = 0, /**< Flat in XY, normal along +Z (billboard/wall face). */
PLANE_AXIS_XZ = 1, /**< Flat in XZ, normal along +Y (ground/floor plane). */
PLANE_AXIS_YZ = 2, /**< Flat in YZ, normal along +X (side wall). */
} planeaxis_t;
extern mesh_t PLANE_MESH_SIMPLE;
extern meshvertex_t PLANE_MESH_SIMPLE_VERTICES[PLANE_VERTEX_COUNT];
/**
* Initializes the simple unit XZ plane mesh (ground plane) from (0,0,0) to
* (1,0,1).
*
* @return Error for initialization of the plane mesh.
*/
errorret_t planeInit();
/**
* Buffers an axis-aligned plane into the provided vertex array.
* Writes PLANE_VERTEX_COUNT (6) vertices (two triangles, CCW winding).
*
* The min/max corners fully describe the plane in 3D space. The axis enum
* controls which dimension is treated as the "depth" (normal) axis:
* PLANE_AXIS_XY: spans X and Y, depth from min[2]/max[2] (uses min[2])
* PLANE_AXIS_XZ: spans X and Z, depth from min[1]/max[1] (uses min[1])
* PLANE_AXIS_YZ: spans Y and Z, depth from min[0]/max[0] (uses min[0])
*
* @param vertices Vertex array to write into (must hold PLANE_VERTEX_COUNT).
* @param axis Which axis the plane's normal points along.
* @param min Minimum XYZ corner.
* @param max Maximum XYZ corner.
* @param color Color applied to all vertices.
* @param uvMin Minimum UV coordinates.
* @param uvMax Maximum UV coordinates.
*/
void planeBuffer(
meshvertex_t *vertices,
const planeaxis_t axis,
const vec3 min,
const vec3 max
#if MESH_ENABLE_COLOR
, const color_t color
#endif
, const vec2 uvMin,
const vec2 uvMax
);
-219
View File
@@ -1,219 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "quad.h"
#include "assert/assert.h"
mesh_t QUAD_MESH_SIMPLE;
meshvertex_t QUAD_MESH_SIMPLE_VERTICES[QUAD_VERTEX_COUNT] = {
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 0.0f, 0.0f },
.pos = { 0.0f, 0.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 1.0f, 0.0f },
.pos = { 1.0f, 0.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 1.0f, 1.0f },
.pos = { 1.0f, 1.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 0.0f, 0.0f },
.pos = { 0.0f, 0.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 1.0f, 1.0f },
.pos = { 1.0f, 1.0f, 0.0f }
},
{
#if MESH_ENABLE_COLOR
.color = COLOR_WHITE_4B,
#endif
.uv = { 0.0f, 1.0f },
.pos = { 0.0f, 1.0f, 0.0f }
}
};
errorret_t quadInit() {
errorChain(meshInit(
&QUAD_MESH_SIMPLE,
QUAD_PRIMITIVE_TYPE,
QUAD_VERTEX_COUNT,
QUAD_MESH_SIMPLE_VERTICES
));
errorOk();
}
void quadBuffer(
meshvertex_t *vertices,
const float_t minX,
const float_t minY,
const float_t maxX,
const float_t maxY,
const float_t u0,
const float_t v0,
const float_t u1,
const float_t v1
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
const float_t z = 0.0f; // Z coordinate for 2D rendering
assertNotNull(vertices, "Vertices cannot be NULL");
// First triangle
#if MESH_ENABLE_COLOR
vertices[0].color = color;
#endif
vertices[0].uv[0] = u0;
vertices[0].uv[1] = v1;
vertices[0].pos[0] = minX;
vertices[0].pos[1] = maxY;
vertices[0].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[1].color = color;
#endif
vertices[1].uv[0] = u1;
vertices[1].uv[1] = v0;
vertices[1].pos[0] = maxX;
vertices[1].pos[1] = minY;
vertices[1].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[2].color = color;
#endif
vertices[2].uv[0] = u0;
vertices[2].uv[1] = v0;
vertices[2].pos[0] = minX;
vertices[2].pos[1] = minY;
vertices[2].pos[2] = z;
// Second triangle
#if MESH_ENABLE_COLOR
vertices[3].color = color;
#endif
vertices[3].uv[0] = u0;
vertices[3].uv[1] = v1;
vertices[3].pos[0] = minX;
vertices[3].pos[1] = maxY;
vertices[3].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[4].color = color;
#endif
vertices[4].uv[0] = u1;
vertices[4].uv[1] = v1;
vertices[4].pos[0] = maxX;
vertices[4].pos[1] = maxY;
vertices[4].pos[2] = z;
#if MESH_ENABLE_COLOR
vertices[5].color = color;
#endif
vertices[5].uv[0] = u1;
vertices[5].uv[1] = v0;
vertices[5].pos[0] = maxX;
vertices[5].pos[1] = minY;
vertices[5].pos[2] = z;
}
void quadBuffer3D(
meshvertex_t *vertices,
const vec3 min,
const vec3 max,
const vec2 uvMin,
const vec2 uvMax
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(min, "Min vector cannot be NULL");
assertNotNull(max, "Max vector cannot be NULL");
assertNotNull(uvMin, "UV Min vector cannot be NULL");
assertNotNull(uvMax, "UV Max vector cannot be NULL");
// First triangle
#if MESH_ENABLE_COLOR
vertices[0].color = color;
#endif
vertices[0].uv[0] = uvMin[0];
vertices[0].uv[1] = uvMin[1];
vertices[0].pos[0] = min[0];
vertices[0].pos[1] = min[1];
vertices[0].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[1].color = color;
#endif
vertices[1].uv[0] = uvMax[0];
vertices[1].uv[1] = uvMin[1];
vertices[1].pos[0] = max[0];
vertices[1].pos[1] = min[1];
vertices[1].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[2].color = color;
#endif
vertices[2].uv[0] = uvMax[0];
vertices[2].uv[1] = uvMax[1];
vertices[2].pos[0] = max[0];
vertices[2].pos[1] = max[1];
vertices[2].pos[2] = min[2];
// Second triangle
#if MESH_ENABLE_COLOR
vertices[3].color = color;
#endif
vertices[3].uv[0] = uvMin[0];
vertices[3].uv[1] = uvMin[1];
vertices[3].pos[0] = min[0];
vertices[3].pos[1] = min[1];
vertices[3].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[4].color = color;
#endif
vertices[4].uv[0] = uvMax[0];
vertices[4].uv[1] = uvMax[1];
vertices[4].pos[0] = max[0];
vertices[4].pos[1] = max[1];
vertices[4].pos[2] = min[2];
#if MESH_ENABLE_COLOR
vertices[5].color = color;
#endif
vertices[5].uv[0] = uvMin[0];
vertices[5].uv[1] = uvMax[1];
vertices[5].pos[0] = min[0];
vertices[5].pos[1] = max[1];
vertices[5].pos[2] = min[2];
}
-73
View File
@@ -1,73 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "mesh.h"
#include "display/color.h"
#define QUAD_VERTEX_COUNT 6
#define QUAD_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t QUAD_MESH_SIMPLE;
extern meshvertex_t QUAD_MESH_SIMPLE_VERTICES[QUAD_VERTEX_COUNT];
/**
* Initializes the quad mesh.
*
* @return Error for initialization of the quad mesh.
*/
errorret_t quadInit();
/**
* Buffers a quad into the provided vertex array.
*
* @param vertices The vertex array to buffer into.
* @param minX The minimum X coordinate of the quad.
* @param minY The minimum Y coordinate of the quad.
* @param maxX The maximum X coordinate of the quad.
* @param maxY The maximum Y coordinate of the quad.
* @param color The color of the quad.
* @param u0 The U texture coordinate for the first vertex.
* @param v0 The V texture coordinate for the first vertex.
* @param u1 The U texture coordinate for the second vertex.
* @param v1 The V texture coordinate for the second vertex.
*/
void quadBuffer(
meshvertex_t *vertices,
const float_t minX,
const float_t minY,
const float_t maxX,
const float_t maxY,
const float_t u0,
const float_t v0,
const float_t u1,
const float_t v1
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
/**
* Buffers a 3D quad into the provided vertex array.
*
* @param vertices The vertex array to buffer into.
* @param min The minimum XYZ coordinates of the quad.
* @param max The maximum XYZ coordinates of the quad.
* @param color The color of the quad.
* @param uvMin The minimum UV coordinates of the quad.
* @param uvMax The maximum UV coordinates of the quad.
*/
void quadBuffer3D(
meshvertex_t *vertices,
const vec3 min,
const vec3 max,
const vec2 uvMin,
const vec2 uvMax
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
-145
View File
@@ -1,145 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "sphere.h"
#include "assert/assert.h"
mesh_t SPHERE_MESH_SIMPLE;
meshvertex_t SPHERE_MESH_SIMPLE_VERTICES[SPHERE_VERTEX_COUNT];
errorret_t sphereInit() {
vec3 center = { 0.0f, 0.0f, 0.0f };
sphereBuffer(
SPHERE_MESH_SIMPLE_VERTICES,
center,
0.5f,
SPHERE_STACKS,
SPHERE_SECTORS
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&SPHERE_MESH_SIMPLE,
SPHERE_PRIMITIVE_TYPE,
SPHERE_VERTEX_COUNT,
SPHERE_MESH_SIMPLE_VERTICES
));
errorOk();
}
void sphereBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const int32_t stacks,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
assertNotNull(center, "Center vector cannot be NULL");
const float_t stackStep = (float_t)GLM_PI / (float_t)stacks;
const float_t sectorStep = 2.0f * (float_t)GLM_PI / (float_t)sectors;
int32_t vi = 0;
for(int32_t i = 0; i < stacks; i++) {
/* Latitude angles: top of band -> bottom of band */
const float_t phi1 = (float_t)GLM_PI_2 - (float_t)i * stackStep;
const float_t phi2 = (float_t)GLM_PI_2 - (float_t)(i + 1) * stackStep;
const float_t y1 = radius * sinf(phi1);
const float_t y2 = radius * sinf(phi2);
const float_t xz1 = radius * cosf(phi1);
const float_t xz2 = radius * cosf(phi2);
const float_t v1 = 1.0f - (float_t)i / (float_t)stacks;
const float_t v2 = 1.0f - (float_t)(i + 1) / (float_t)stacks;
for(int32_t j = 0; j < sectors; j++) {
const float_t theta1 = (float_t)j * sectorStep;
const float_t theta2 = (float_t)(j + 1) * sectorStep;
const float_t x11 = xz1 * cosf(theta1);
const float_t z11 = xz1 * sinf(theta1);
const float_t x12 = xz1 * cosf(theta2);
const float_t z12 = xz1 * sinf(theta2);
const float_t x21 = xz2 * cosf(theta1);
const float_t z21 = xz2 * sinf(theta1);
const float_t x22 = xz2 * cosf(theta2);
const float_t z22 = xz2 * sinf(theta2);
const float_t u1 = (float_t)j / (float_t)sectors;
const float_t u2 = (float_t)(j + 1) / (float_t)sectors;
/* Triangle 1: top-left, bottom-left, top-right */
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x11;
vertices[vi].pos[1] = center[1] + y1;
vertices[vi].pos[2] = center[2] + z11;
vertices[vi].uv[0] = u1;
vertices[vi].uv[1] = v1;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x21;
vertices[vi].pos[1] = center[1] + y2;
vertices[vi].pos[2] = center[2] + z21;
vertices[vi].uv[0] = u1;
vertices[vi].uv[1] = v2;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x12;
vertices[vi].pos[1] = center[1] + y1;
vertices[vi].pos[2] = center[2] + z12;
vertices[vi].uv[0] = u2;
vertices[vi].uv[1] = v1;
vi++;
/* Triangle 2: top-right, bottom-left, bottom-right */
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x12;
vertices[vi].pos[1] = center[1] + y1;
vertices[vi].pos[2] = center[2] + z12;
vertices[vi].uv[0] = u2;
vertices[vi].uv[1] = v1;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x21;
vertices[vi].pos[1] = center[1] + y2;
vertices[vi].pos[2] = center[2] + z21;
vertices[vi].uv[0] = u1;
vertices[vi].uv[1] = v2;
vi++;
#if MESH_ENABLE_COLOR
vertices[vi].color = color;
#endif
vertices[vi].pos[0] = center[0] + x22;
vertices[vi].pos[1] = center[1] + y2;
vertices[vi].pos[2] = center[2] + z22;
vertices[vi].uv[0] = u2;
vertices[vi].uv[1] = v2;
vi++;
}
}
}
-47
View File
@@ -1,47 +0,0 @@
/**
* 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 SPHERE_STACKS 8
#define SPHERE_SECTORS 16
#define SPHERE_VERTEX_COUNT (SPHERE_STACKS * SPHERE_SECTORS * 6)
#define SPHERE_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t SPHERE_MESH_SIMPLE;
extern meshvertex_t SPHERE_MESH_SIMPLE_VERTICES[SPHERE_VERTEX_COUNT];
/**
* Initializes the simple unit sphere mesh centered at (0,0,0) with radius 0.5.
*
* @return Error for initialization of the sphere mesh.
*/
errorret_t sphereInit();
/**
* Buffers a UV sphere into the provided vertex array.
* Writes stacks * sectors * 6 vertices (CCW winding).
*
* @param vertices Vertex array to write into (must hold stacks*sectors*6).
* @param center Center position of the sphere.
* @param radius Radius of the sphere.
* @param stacks Number of horizontal rings (latitude bands).
* @param sectors Number of vertical segments (longitude slices).
* @param color Color applied to all vertices.
*/
void sphereBuffer(
meshvertex_t *vertices,
const vec3 center,
const float_t radius,
const int32_t stacks,
const int32_t sectors
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
-106
View File
@@ -1,106 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "triprism.h"
#include "assert/assert.h"
mesh_t TRIPRISM_MESH_SIMPLE;
meshvertex_t TRIPRISM_MESH_SIMPLE_VERTICES[TRIPRISM_VERTEX_COUNT];
errorret_t triPrismInit() {
triPrismBuffer(
TRIPRISM_MESH_SIMPLE_VERTICES,
0.0f, 0.0f, /* p0: bottom-left */
1.0f, 0.0f, /* p1: bottom-right */
0.5f, 1.0f, /* p2: apex */
0.0f, 1.0f /* minZ, maxZ */
#if MESH_ENABLE_COLOR
, COLOR_WHITE_4B
#endif
);
errorChain(meshInit(
&TRIPRISM_MESH_SIMPLE,
TRIPRISM_PRIMITIVE_TYPE,
TRIPRISM_VERTEX_COUNT,
TRIPRISM_MESH_SIMPLE_VERTICES
));
errorOk();
}
void triPrismBuffer(
meshvertex_t *vertices,
const float_t x0, const float_t y0,
const float_t x1, const float_t y1,
const float_t x2, const float_t y2,
const float_t minZ,
const float_t maxZ
#if MESH_ENABLE_COLOR
, const color_t color
#endif
) {
assertNotNull(vertices, "Vertices cannot be NULL");
/* Helper macro: write one vertex then advance index. */
int32_t vi = 0;
#if MESH_ENABLE_COLOR
#define PRISM_VERT(px, py, pz, u, v) \
vertices[vi].color = color; \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#else
#define PRISM_VERT(px, py, pz, u, v) \
vertices[vi].pos[0] = (px); \
vertices[vi].pos[1] = (py); \
vertices[vi].pos[2] = (pz); \
vertices[vi].uv[0] = (u); \
vertices[vi].uv[1] = (v); \
vi++;
#endif
/* --- Front face (z = maxZ), CCW from +Z --- */
PRISM_VERT(x0, y0, maxZ, 0.0f, 0.0f)
PRISM_VERT(x1, y1, maxZ, 1.0f, 0.0f)
PRISM_VERT(x2, y2, maxZ, 0.5f, 1.0f)
/* --- Back face (z = minZ), CCW from -Z (reverse winding) --- */
PRISM_VERT(x2, y2, minZ, 0.5f, 1.0f)
PRISM_VERT(x1, y1, minZ, 1.0f, 0.0f)
PRISM_VERT(x0, y0, minZ, 0.0f, 0.0f)
/* --- Side face 0: edge p0->p1 --- */
PRISM_VERT(x0, y0, minZ, 0.0f, 0.0f)
PRISM_VERT(x1, y1, minZ, 1.0f, 0.0f)
PRISM_VERT(x1, y1, maxZ, 1.0f, 1.0f)
PRISM_VERT(x0, y0, minZ, 0.0f, 0.0f)
PRISM_VERT(x1, y1, maxZ, 1.0f, 1.0f)
PRISM_VERT(x0, y0, maxZ, 0.0f, 1.0f)
/* --- Side face 1: edge p1->p2 --- */
PRISM_VERT(x1, y1, minZ, 0.0f, 0.0f)
PRISM_VERT(x2, y2, minZ, 1.0f, 0.0f)
PRISM_VERT(x2, y2, maxZ, 1.0f, 1.0f)
PRISM_VERT(x1, y1, minZ, 0.0f, 0.0f)
PRISM_VERT(x2, y2, maxZ, 1.0f, 1.0f)
PRISM_VERT(x1, y1, maxZ, 0.0f, 1.0f)
/* --- Side face 2: edge p2->p0 --- */
PRISM_VERT(x2, y2, minZ, 0.0f, 0.0f)
PRISM_VERT(x0, y0, minZ, 1.0f, 0.0f)
PRISM_VERT(x0, y0, maxZ, 1.0f, 1.0f)
PRISM_VERT(x2, y2, minZ, 0.0f, 0.0f)
PRISM_VERT(x0, y0, maxZ, 1.0f, 1.0f)
PRISM_VERT(x2, y2, maxZ, 0.0f, 1.0f)
#undef PRISM_VERT
}
-50
View File
@@ -1,50 +0,0 @@
/**
* 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 TRIPRISM_VERTEX_COUNT 24
#define TRIPRISM_PRIMITIVE_TYPE MESH_PRIMITIVE_TYPE_TRIANGLES
extern mesh_t TRIPRISM_MESH_SIMPLE;
extern meshvertex_t TRIPRISM_MESH_SIMPLE_VERTICES[TRIPRISM_VERTEX_COUNT];
/**
* Initializes the simple unit triangular prism. The cross-section triangle has
* vertices (0,0), (1,0), (0.5,1) in XY, extruded from z=0 to z=1.
*
* @return Error for initialization of the triangular prism mesh.
*/
errorret_t triPrismInit();
/**
* Buffers a triangular prism into the provided vertex array.
* The triangular cross-section is defined by three 2D points in the XY plane;
* the prism is extruded along the Z axis between minZ and maxZ.
* Writes TRIPRISM_VERTEX_COUNT (24) vertices (CCW winding).
*
* @param vertices Vertex array to write into.
* @param x0,y0 First triangle vertex (XY).
* @param x1,y1 Second triangle vertex (XY).
* @param x2,y2 Third triangle vertex (XY).
* @param minZ Near Z extent of the prism.
* @param maxZ Far Z extent of the prism.
* @param color Color applied to all vertices.
*/
void triPrismBuffer(
meshvertex_t *vertices,
const float_t x0, const float_t y0,
const float_t x1, const float_t y1,
const float_t x2, const float_t y2,
const float_t minZ,
const float_t maxZ
#if MESH_ENABLE_COLOR
, const color_t color
#endif
);
-403
View File
@@ -1,403 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "screen.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "display/mesh/quad.h"
#include "display/shader/shaderunlit.h"
screen_t SCREEN;
errorret_t screenInit() {
memoryZero(&SCREEN, sizeof(screen_t));
SCREEN.background = COLOR_CORNFLOWER_BLUE;
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
SCREEN.mode = SCREEN_MODE_FIXED_VIEWPORT_HEIGHT;
SCREEN.fixedHeight.height = DUSK_DISPLAY_SCREEN_HEIGHT;
quadBuffer(
SCREEN.frameBufferMeshVertices,
0.0f, 0.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f
#if MESH_ENABLE_COLOR
, COLOR_WHITE
#endif
);
errorChain(meshInit(
&SCREEN.frameBufferMesh,
QUAD_PRIMITIVE_TYPE,
QUAD_VERTEX_COUNT,
SCREEN.frameBufferMeshVertices
));
#endif
// Init screen to backbuffer mode by default
errorChain(screenBind());
errorOk();
}
errorret_t screenBind() {
// Assume backbuffer is currently bound.
switch(SCREEN.mode) {
case SCREEN_MODE_BACKBUFFER: {
// Screen mode backbuffer uses the full display size
SCREEN.width = frameBufferGetWidth(FRAMEBUFFER_BOUND);
SCREEN.height = frameBufferGetHeight(FRAMEBUFFER_BOUND);
SCREEN.aspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
// No needd for a framebuffer.
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
if(SCREEN.framebufferReady) {
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
#endif
break;
}
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
case SCREEN_MODE_FIXED_SIZE: {
SCREEN.width = SCREEN.fixedSize.width;
SCREEN.height = SCREEN.fixedSize.height;
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
if(SCREEN.framebufferReady) {
// Is current framebuffer the correct size?
int32_t curFbWidth, curFbHeight;
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
if(curFbWidth == SCREEN.width && curFbHeight == SCREEN.height) {
// Correct size, nothing to do.
errorChain(frameBufferBind(&SCREEN.framebuffer));
errorOk();
}
// Need a new framebuffer.
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
// Create new framebuffer
errorChain(frameBufferInit(
&SCREEN.framebuffer, SCREEN.width, SCREEN.height
));
SCREEN.framebufferReady = true;
errorChain(frameBufferBind(&SCREEN.framebuffer));
break;
}
case SCREEN_MODE_ASPECT_RATIO: {
// Aspect ratio mode, requires a framebuffer.
int32_t fbWidth, fbHeight;
fbWidth = frameBufferGetWidth(FRAMEBUFFER_BOUND);
fbHeight = frameBufferGetHeight(FRAMEBUFFER_BOUND);
float_t currentAspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
if(currentAspect == SCREEN.aspectRatio.ratio) {
// No need to use framebuffer.
SCREEN.width = fbWidth;
SCREEN.height = fbHeight;
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
if(SCREEN.framebufferReady) {
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
errorOk();
}
int32_t newFbWidth, newFbHeight;
if(currentAspect > SCREEN.aspectRatio.ratio) {
// Wider than target aspect, limit by height
newFbWidth = (int32_t)floorf(fbHeight * SCREEN.aspectRatio.ratio);
newFbHeight = (int32_t)fbHeight;
} else {
// Taller than target aspect, limit by width
newFbHeight = (int32_t)floorf(fbWidth / SCREEN.aspectRatio.ratio);
newFbWidth = (int32_t)fbWidth;
}
if(SCREEN.framebufferReady) {
// Is current framebuffer the correct size?
int32_t curFbWidth, curFbHeight;
float_t curFbAspect = frameBufferGetAspect(&SCREEN.framebuffer);
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
// Correct size, nothing to do.
SCREEN.width = newFbWidth;
SCREEN.height = newFbHeight;
SCREEN.aspect = curFbAspect;
errorChain(frameBufferBind(&SCREEN.framebuffer));
errorOk();
}
// Need a new framebuffer.
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
// Create new framebuffer
errorChain(frameBufferInit(
&SCREEN.framebuffer, newFbWidth, newFbHeight
));
SCREEN.width = newFbWidth;
SCREEN.height = newFbHeight;
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
SCREEN.framebufferReady = true;
// Bind FB
errorChain(frameBufferBind(&SCREEN.framebuffer));
break;
}
case SCREEN_MODE_FIXED_HEIGHT: {
float_t fbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
float_t fbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
float_t fbAspect = fbWidth / fbHeight;
int32_t newFbWidth, newFbHeight;
newFbHeight = SCREEN.fixedHeight.height;
newFbWidth = (int32_t)floorf(newFbHeight * fbAspect);
SCREEN.width = newFbWidth;
SCREEN.height = newFbHeight;
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
if(fbWidth == newFbWidth && fbHeight == newFbHeight) {
// No need to use framebuffer.
if(SCREEN.framebufferReady) {
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
errorOk();
}
if(SCREEN.framebufferReady) {
// Is current framebuffer the correct size?
int32_t curFbWidth, curFbHeight;
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
errorChain(frameBufferBind(&SCREEN.framebuffer));
errorOk();
}
// Need a new framebuffer.
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
// Create a new framebuffer.
errorChain(frameBufferInit(
&SCREEN.framebuffer, newFbWidth, newFbHeight
));
SCREEN.framebufferReady = true;
errorChain(frameBufferBind(&SCREEN.framebuffer));
break;
}
case SCREEN_MODE_FIXED_WIDTH: {
float_t fbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
float_t fbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
float_t fbAspect = fbWidth / fbHeight;
int32_t newFbWidth, newFbHeight;
newFbWidth = SCREEN.fixedWidth.width;
newFbHeight = (int32_t)floorf(newFbWidth / fbAspect);
SCREEN.width = newFbWidth;
SCREEN.height = newFbHeight;
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
if(fbWidth == newFbWidth && fbHeight == newFbHeight) {
// No need to use framebuffer.
if(SCREEN.framebufferReady) {
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
errorOk();
}
if(SCREEN.framebufferReady) {
// Is current framebuffer the correct size?
int32_t curFbWidth, curFbHeight;
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
errorChain(frameBufferBind(&SCREEN.framebuffer));
errorOk();
}
// Need a new framebuffer.
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
// Create a new framebuffer.
errorChain(frameBufferInit(
&SCREEN.framebuffer, newFbWidth, newFbHeight
));
SCREEN.framebufferReady = true;
errorChain(frameBufferBind(&SCREEN.framebuffer));
break;
}
case SCREEN_MODE_FIXED_VIEWPORT_HEIGHT: {
SCREEN.height = SCREEN.fixedViewportHeight.height;
float_t fbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
float_t fbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
float_t fbAspect = fbWidth / fbHeight;
SCREEN.width = (int32_t)floorf(SCREEN.height * fbAspect);
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
break;
}
#endif
default: {
assertUnreachable("Invalid screen mode.");
break;
}
}
errorOk();
}
errorret_t screenUnbind() {
switch(SCREEN.mode) {
// Nothing to do here.
case SCREEN_MODE_BACKBUFFER:
break;
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
case SCREEN_MODE_ASPECT_RATIO:
case SCREEN_MODE_FIXED_HEIGHT:
case SCREEN_MODE_FIXED_SIZE:
case SCREEN_MODE_FIXED_WIDTH:
if(SCREEN.framebufferReady) {
errorChain(frameBufferBind(NULL));
}
break;
case SCREEN_MODE_FIXED_VIEWPORT_HEIGHT:
break;
#endif
default:
assertUnreachable("Invalid screen mode.");
break;
}
errorOk();
}
errorret_t screenRender() {
if(SCREEN.mode == SCREEN_MODE_BACKBUFFER) {
errorOk();
}
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
if(SCREEN.mode == SCREEN_MODE_FIXED_VIEWPORT_HEIGHT) {
glViewport(0, 0, SCREEN.width, SCREEN.height);
errorOk();
}
if(
SCREEN.mode == SCREEN_MODE_ASPECT_RATIO ||
SCREEN.mode == SCREEN_MODE_FIXED_HEIGHT ||
SCREEN.mode == SCREEN_MODE_FIXED_SIZE ||
SCREEN.mode == SCREEN_MODE_FIXED_WIDTH
) {
if(!SCREEN.framebufferReady) {
// Nothing to do here.
errorOk();
}
float_t bbWidth, bbHeight;
bbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
bbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
float_t backBufferAspect = bbWidth / bbHeight;
// Determine framebuffer centering
float_t fbWidth, fbHeight, fbAspect;
float_t fbX, fbY;
fbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
fbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
fbAspect = fbWidth / fbHeight;
if(backBufferAspect > fbAspect) {
fbHeight = bbHeight;
fbWidth = fbHeight * fbAspect;
fbX = (bbWidth - fbWidth) * 0.5f;
fbY = 0.0f;
} else {
fbWidth = bbWidth;
fbHeight = fbWidth / fbAspect;
fbX = 0.0f;
fbY = (bbHeight - fbHeight) * 0.5f;
}
// Determine back buffer matricies
float_t centerX = bbWidth * 0.5f;
float_t centerY = bbHeight * 0.5f;
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
centerX + fbWidth * 0.5f, centerY - fbHeight * 0.5f, // bottom-right
0.0f, 0.0f,
1.0f, 1.0f
#if MESH_ENABLE_COLOR
, COLOR_WHITE
#endif
);
frameBufferClear(
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
COLOR_BLACK
);
shaderBind(&SHADER_UNLIT);
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj);
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view);
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model);
// errorChain(textureBind(&SCREEN.framebuffer.texture));
errorChain(meshDraw(&SCREEN.frameBufferMesh, 0, -1));
errorOk();
};
#endif
assertUnreachable("Invalid screen mode.");
errorThrow("Invalid screen mode.");
}
errorret_t screenDispose() {
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
if(SCREEN.framebufferReady) {
errorChain(frameBufferDispose(&SCREEN.framebuffer));
SCREEN.framebufferReady = false;
}
#endif
errorOk();
}
-116
View File
@@ -1,116 +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"
#include "display/framebuffer/framebuffer.h"
#include "display/mesh/quad.h"
#include "display/color.h"
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
#ifndef DUSK_DISPLAY_SCREEN_HEIGHT
#error "DUSK_DISPLAY_SCREEN_HEIGHT must be defined"
#endif
#endif
typedef enum {
SCREEN_MODE_BACKBUFFER,
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
SCREEN_MODE_FIXED_SIZE,
SCREEN_MODE_ASPECT_RATIO,// Maintains aspect at all cost
SCREEN_MODE_FIXED_HEIGHT, // Fixed height, width expands/contracts as needed
SCREEN_MODE_FIXED_WIDTH, // Fixed width, height expands/contracts as needed
// Fixed viewport height. Fixed height but higher resolution.
SCREEN_MODE_FIXED_VIEWPORT_HEIGHT,
#endif
} screenmode_t;
// typedef enum {
// SCREEN_SCALE_MODE_FILL,
// SCREEN_SCALE_MODE_INTEGER,
// SCREEN_SCALE_MODE_INEGER_OVERFLOW
// } screenscalemode_t;
typedef struct {
screenmode_t mode;
// screenscalemode_t scaleMode;
// Calculated dimensions of the viewport, to be used by the camera
int32_t width;
int32_t height;
float_t aspect;
color_t background;
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
framebuffer_t framebuffer;
bool_t framebufferReady;
// camera_t framebufferCamera;
mesh_t frameBufferMesh;
meshvertex_t frameBufferMeshVertices[QUAD_VERTEX_COUNT];
#endif
union {
struct {
int32_t width;
int32_t height;
} fixedSize;
struct {
float_t ratio;
} aspectRatio;
struct {
int32_t height;
} fixedHeight;
struct {
int32_t width;
} fixedWidth;
struct {
int32_t height;
} fixedViewportHeight;
};
} screen_t;
extern screen_t SCREEN;
/**
* Initializes the screen system.
*
* @return Error code and state, if error occurs.
*/
errorret_t screenInit();
/**
* Binds the screen, this is done before rendering game content.
*
* @return Error code and state, if error occurs.
*/
errorret_t screenBind();
/**
* Unbinds the screen, does nothing for now.
*
* @return Error code and state, if error occurs.
*/
errorret_t screenUnbind();
/**
* Renders the screen to the current framebuffer.
*
* @return Error code and state, if error occurs.
*/
errorret_t screenRender();
/**
* Disposes the screen system.
*
* @return Error code and state, if error occurs.
*/
errorret_t screenDispose();
-12
View File
@@ -1,12 +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
shader.c
shaderlist.c
shaderunlit.c
)
-83
View File
@@ -1,83 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "shader.h"
#include "shadermaterial.h"
#include "assert/assert.h"
#include "log/log.h"
shader_t *bound = NULL;
errorret_t shaderInit(shader_t *shader, const shaderdefinition_t *def) {
assertNotNull(shader, "Shader cannot be null");
errorChain(shaderInitPlatform(shader, def));
bound = NULL;
errorOk();
}
errorret_t shaderBind(shader_t *shader) {
assertNotNull(shader, "Shader cannot be null");
errorChain(shaderBindPlatform(shader));
bound = shader;
errorOk();
}
errorret_t shaderSetMatrix(
shader_t *shader,
const char_t *name,
mat4 matrix
) {
assertNotNull(shader, "Shader cannot be null");
assertStrLenMin(name, 1, "Uniform name cannot be empty");
assertNotNull(matrix, "Matrix cannot be null");
assertTrue(bound == shader, "Shader must be bound.");
errorChain(shaderSetMatrixPlatform(shader, name, matrix));
errorOk();
}
errorret_t shaderSetTexture(
shader_t *shader,
const char_t *name,
texture_t *texture
) {
assertNotNull(shader, "Shader cannot be null");
assertStrLenMin(name, 1, "Uniform name cannot be empty");
assertTrue(bound == shader, "Shader must be bound.");
errorChain(shaderSetTexturePlatform(shader, name, texture));
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");
assertTrue(bound == shader, "Shader must be bound.");
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");
bound = NULL;
errorChain(shaderDisposePlatform(shader));
errorOk();
}
-108
View File
@@ -1,108 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/texture/texture.h"
#include "display/shader/shaderplatform.h"
#ifndef shaderInitPlatform
#error "shaderInitPlatform must be defined to use shader.h"
#endif
#ifndef shaderBindPlatform
#error "shaderBindPlatform must be defined to use shader.h"
#endif
#ifndef shaderSetMatrixPlatform
#error "shaderSetMatrixPlatform must be defined to use shader.h"
#endif
#ifndef shaderDisposePlatform
#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;
/**
* Initializes a shader. This is platform dependant.
*
* @param shader Shader to initialize
* @param def Definition of the shader to initialize with.
* @return Error if failure, otherwise errorOk
*/
errorret_t shaderInit(shader_t *shader, const shaderdefinition_t *def);
/**
* Binds a shader. This is platform dependant.
*
* @param shader Shader to bind
* @return Error if failure, otherwise errorOk
*/
errorret_t shaderBind(shader_t *shader);
/**
* Sets a matrix uniform in the shader. This is platform dependant.
*
* @param shader Shader to set the matrix in
* @param name Name of the uniform to set
* @param matrix Matrix to set
* @return Error if failure, otherwise errorOk
*/
errorret_t shaderSetMatrix(
shader_t *shader,
const char_t *name,
mat4 matrix
);
/**
* Sets a texture uniform in the shader. This is platform dependant.
*
* @param shader Shader to set the texture in
* @param name Name of the uniform to set
* @param texture Texture to set
* @return Error if failure, otherwise errorOk
*/
errorret_t shaderSetTexture(
shader_t *shader,
const char_t *name,
texture_t *texture
);
/**
* Sets a color uniform in the shader. This is platform dependant.
*
* @param shader Shader to set the color in
* @param name Name of the uniform to set
* @param color Color to set
* @return Error if failure, otherwise errorOk
*/
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
*/
errorret_t shaderDispose(shader_t *shader);
-87
View File
@@ -1,87 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "shaderlist.h"
#include "display/screen/screen.h"
#include "assert/assert.h"
shaderlistdef_t SHADER_LIST_DEFS[SHADER_LIST_SHADER_COUNT] = {
[SHADER_LIST_SHADER_UNLIT] = {
.shader = &SHADER_UNLIT,
.definition = &SHADER_UNLIT_DEFINITION
},
};
errorret_t shaderListInit() {
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
);
glm_perspective(
glm_rad(45.0f),
SCREEN.aspect,
0.1f,
100.0f,
proj
);
glm_mat4_identity(model);
for(shaderlistshader_t i = 0; i < SHADER_LIST_SHADER_COUNT; i++) {
if(i == SHADER_LIST_SHADER_NULL) {
continue;
}
assertNotNull(
SHADER_LIST_DEFS[i].shader, "Shader cannot be null"
);
assertNotNull(
SHADER_LIST_DEFS[i].definition, "Shader definition cannot be null"
);
errorChain(shaderInit(
SHADER_LIST_DEFS[i].shader, SHADER_LIST_DEFS[i].definition
));
errorChain(shaderBind(SHADER_LIST_DEFS[i].shader));
errorChain(shaderSetMatrix(
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_PROJECTION, proj
));
errorChain(shaderSetMatrix(
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_VIEW, view
));
errorChain(shaderSetMatrix(
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_MODEL, model
));
errorChain(shaderSetTexture(
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_TEXTURE, NULL
));
errorChain(shaderSetColor(
SHADER_LIST_DEFS[i].shader, SHADER_UNLIT_COLOR, COLOR_WHITE
));
}
errorOk();
}
errorret_t shaderListDispose(void) {
for(shaderlistshader_t i = 0; i < SHADER_LIST_SHADER_COUNT; i++) {
if(i == SHADER_LIST_SHADER_NULL) {
continue;
}
assertNotNull(
SHADER_LIST_DEFS[i].shader, "Shader cannot be null"
);
errorChain(shaderDispose(SHADER_LIST_DEFS[i].shader));
}
errorOk();
}
-40
View File
@@ -1,40 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/shader/shader.h"
#include "display/shader/shaderunlit.h"
typedef enum {
SHADER_LIST_SHADER_NULL,
SHADER_LIST_SHADER_UNLIT,
SHADER_LIST_SHADER_COUNT
} shaderlistshader_t;
typedef struct {
shader_t *shader;
shaderdefinition_t *definition;
} shaderlistdef_t;
extern shaderlistdef_t SHADER_LIST_DEFS[SHADER_LIST_SHADER_COUNT];
/**
* Initializes all default shaders and uploads the initial view, projection,
* and model matrices to each.
*
* @return Error state.
*/
errorret_t shaderListInit();
/**
* Disposes all default shaders.
*
* @return Error state.
*/
errorret_t shaderListDispose(void);
-13
View File
@@ -1,13 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/shader/shaderlist.h"
typedef union shadermaterial_u {
shaderunlitmaterial_t unlit;
} shadermaterial_t;
-32
View File
@@ -1,32 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "shaderunlit.h"
#include "display/shader/shadermaterial.h"
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();
}
-35
View File
@@ -1,35 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "shader.h"
#define SHADER_UNLIT_PROJECTION "u_Proj"
#define SHADER_UNLIT_VIEW "u_View"
#define SHADER_UNLIT_MODEL "u_Model"
#define SHADER_UNLIT_TEXTURE "u_Texture"
#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;
/**
* 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
);
@@ -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
spritebatch.c
)
-179
View File
@@ -1,179 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "spritebatch.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/math.h"
#include "display/shader/shadermaterial.h"
meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];
spritebatch_t SPRITEBATCH;
errorret_t spriteBatchInit() {
memoryZero(&SPRITEBATCH, sizeof(spritebatch_t));
errorChain(meshInit(
&SPRITEBATCH.mesh,
QUAD_PRIMITIVE_TYPE,
SPRITEBATCH_VERTEX_COUNT,
SPRITEBATCH_VERTICES
));
errorOk();
}
errorret_t spriteBatchBuffer(
const spritebatchsprite_t *sprites,
const uint32_t count,
shader_t *shader,
const shadermaterial_t material
) {
assertNotNull(sprites, "Sprites cannot be null");
assertTrue(count > 0, "Count must be greater than zero");
assertNotNull(shader, "Shader cannot be null");
// Did the shader or material data change?
if(shader != SPRITEBATCH.shader) {
errorChain(spriteBatchFlush());
SPRITEBATCH.shader = shader;
SPRITEBATCH.material = material;
} else if(memoryCompare(
&material, &SPRITEBATCH.material, sizeof(shadermaterial_t)
) != 0) {
// Did the material data change?
errorChain(spriteBatchFlush());
SPRITEBATCH.shader = shader;
SPRITEBATCH.material = material;
}
// Buffer the vertices.
uint32_t remaining = count;
do {
uint32_t spritesBeforeFlush = (
SPRITEBATCH_SPRITES_MAX_PER_FLUSH - SPRITEBATCH.spriteCount
);
if(spritesBeforeFlush == 0) {
// Flush if we have no capacity before flushing.
errorChain(spriteBatchFlush());
spritesBeforeFlush = SPRITEBATCH_SPRITES_MAX_PER_FLUSH;
}
// Many we buffering?
const uint32_t batchCount = mathMin(
remaining,
spritesBeforeFlush
);
// Destination
meshvertex_t *v = &SPRITEBATCH_VERTICES[
(SPRITEBATCH.spriteCount + (SPRITEBATCH.spriteFlush *
SPRITEBATCH_SPRITES_MAX_PER_FLUSH)) * QUAD_VERTEX_COUNT
];
// Buffer to the mesh vertices.
spriteBatchBufferToMesh(
sprites, batchCount, v, batchCount * QUAD_VERTEX_COUNT
);
SPRITEBATCH.spriteCount += batchCount;
remaining -= batchCount;
} while(remaining > 0);
errorOk();
}
void spriteBatchBufferToMesh(
const spritebatchsprite_t *sprites,
const uint32_t count,
meshvertex_t *vertices,
const uint32_t verticesSize
) {
assertNotNull(sprites, "Sprites cannot be null");
assertTrue(count > 0, "Count must be greater than zero");
assertNotNull(vertices, "Vertices cannot be null");
assertTrue(
verticesSize >= count * QUAD_VERTEX_COUNT, "Vertices array too small"
);
for(uint32_t i = 0; i < count; i++ ){
spritebatchsprite_t sprite = sprites[i];
meshvertex_t *v = &vertices[i * QUAD_VERTEX_COUNT];
// Buffer the quad
v[0].pos[0] = sprite.min[0];
v[0].pos[1] = sprite.min[1];
v[0].pos[2] = sprite.min[2];
v[0].uv[0] = sprite.uvMin[0];
v[0].uv[1] = sprite.uvMin[1];
v[1].pos[0] = sprite.max[0];
v[1].pos[1] = sprite.min[1];
v[1].pos[2] = sprite.min[2];
v[1].uv[0] = sprite.uvMax[0];
v[1].uv[1] = sprite.uvMin[1];
v[2].pos[0] = sprite.max[0];
v[2].pos[1] = sprite.max[1];
v[2].pos[2] = sprite.max[2];
v[2].uv[0] = sprite.uvMax[0];
v[2].uv[1] = sprite.uvMax[1];
v[3].pos[0] = sprite.min[0];
v[3].pos[1] = sprite.min[1];
v[3].pos[2] = sprite.min[2];
v[3].uv[0] = sprite.uvMin[0];
v[3].uv[1] = sprite.uvMin[1];
v[4].pos[0] = sprite.max[0];
v[4].pos[1] = sprite.max[1];
v[4].pos[2] = sprite.max[2];
v[4].uv[0] = sprite.uvMax[0];
v[4].uv[1] = sprite.uvMax[1];
v[5].pos[0] = sprite.min[0];
v[5].pos[1] = sprite.max[1];
v[5].pos[2] = sprite.max[2];
v[5].uv[0] = sprite.uvMin[0];
v[5].uv[1] = sprite.uvMax[1];
}
}
void spriteBatchClear() {
SPRITEBATCH.spriteCount = 0;
SPRITEBATCH.spriteFlush = 0;
SPRITEBATCH.shader = NULL;
memoryZero(&SPRITEBATCH.material, sizeof(shadermaterial_t));
}
errorret_t spriteBatchFlush() {
if(SPRITEBATCH.spriteCount == 0) {
errorOk();
}
size_t vertexCount = QUAD_VERTEX_COUNT * SPRITEBATCH.spriteCount;
size_t vertexOffset = (
SPRITEBATCH.spriteFlush * SPRITEBATCH_SPRITES_MAX_PER_FLUSH *
QUAD_VERTEX_COUNT
);
errorChain(shaderBind(SPRITEBATCH.shader));
errorChain(shaderSetMaterial(SPRITEBATCH.shader, &SPRITEBATCH.material));
errorChain(meshFlush(&SPRITEBATCH.mesh, vertexOffset, vertexCount));
errorChain(meshDraw(&SPRITEBATCH.mesh, vertexOffset, vertexCount));
SPRITEBATCH.spriteFlush++;
if(SPRITEBATCH.spriteFlush >= SPRITEBATCH_FLUSH_COUNT) {
SPRITEBATCH.spriteFlush = 0;
}
SPRITEBATCH.spriteCount = 0;
errorOk();
}
errorret_t spriteBatchDispose() {
errorChain(meshDispose(&SPRITEBATCH.mesh));
errorOk();
}
-107
View File
@@ -1,107 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/mesh/quad.h"
#include "display/texture/texture.h"
#include "display/shader/shadermaterial.h"
#define SPRITEBATCH_SPRITES_MAX 512
#define SPRITEBATCH_VERTEX_COUNT (SPRITEBATCH_SPRITES_MAX * QUAD_VERTEX_COUNT)
#define SPRITEBATCH_FLUSH_COUNT 16
#define SPRITEBATCH_SPRITES_MAX_PER_FLUSH (\
SPRITEBATCH_SPRITES_MAX / SPRITEBATCH_FLUSH_COUNT \
)
typedef struct {
vec3 min;
vec3 max;
vec2 uvMin;
vec2 uvMax;
} spritebatchsprite_t;
typedef struct {
mesh_t mesh;
int32_t spriteCount;
int32_t spriteFlush;
shader_t *shader;
shadermaterial_t material;
} spritebatch_t;
// Have to define these separately because of alignment on certain platforms.
extern meshvertex_t SPRITEBATCH_VERTICES[SPRITEBATCH_VERTEX_COUNT];
extern spritebatch_t SPRITEBATCH;
/**
* Initializes the global sprite batch and its internal mesh buffer.
*
* @return Error state.
*/
errorret_t spriteBatchInit();
/**
* Lowest-level buffer function. Writes sprites into the internal vertex buffer.
* Flushes automatically when the per-flush capacity is reached. Does not
* modify material state - call spriteBatchSetState or use a high-level push
* function before buffering.
*
* @param sprites Pointer to the sprite array.
* @param count Number of sprites to buffer.
* @param shader Shader to use when flushing.
* @param material Material information passed to the shader when flushing.
* @return Error state.
*/
errorret_t spriteBatchBuffer(
const spritebatchsprite_t *sprites,
const uint32_t count,
shader_t *shader,
const shadermaterial_t material
);
/**
* Buffers an array of sprites to a given array of mesh vertices. This is the
* internal method that is used to buffer to the internal spritebatch mesh, but
* you can use it to achieve sprite buffering to a mesh you own.
*
* verticesSize is the size of the vertices array, we use this to ensure no
* buffer overflows.
*
* @param sprites Pointer to the sprite array.
* @param count Number of sprites to buffer.
* @param vertices Pointer to the vertex array to write to.
* @param verticesSize Size of the vertex array, in number of vertices.
*/
void spriteBatchBufferToMesh(
const spritebatchsprite_t *sprites,
const uint32_t count,
meshvertex_t *vertices,
const uint32_t verticesSize
);
/**
* Resets sprite and flush counters and clears the current material state.
* Calling spriteBatchFlush after this renders nothing.
*/
void spriteBatchClear();
/**
* Uploads and draws all buffered sprites. If a material type has been set via
* spriteBatchSetState or spriteBatchCheckState, the shader is bound and the
* material is applied first. If matType is NULL the caller is responsible for
* having the correct shader already bound. Does nothing if the buffer is empty.
*
* @return Error state.
*/
errorret_t spriteBatchFlush();
/**
* Disposes of the sprite batch and frees its internal mesh buffer.
*
* @return Error state.
*/
errorret_t spriteBatchDispose();
-15
View File
@@ -1,15 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/texture/texture.h"
#include "display/texture/tileset.h"
typedef struct {
texture_t *texture;
tileset_t *tileset;
} font_t;
-148
View File
@@ -1,148 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "text.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "display/spritebatch/spritebatch.h"
#include "asset/asset.h"
#include "asset/loader/display/assettextureloader.h"
#include "asset/loader/display/assettilesetloader.h"
#include "display/shader/shaderunlit.h"
font_t FONT_DEFAULT;
errorret_t textInit(void) {
assetloaderinput_t input = { .texture = TEXTURE_FORMAT_RGBA };
assetentry_t *entryTexture = assetLock(
"ui/minogram.png", ASSET_LOADER_TYPE_TEXTURE, &input
);
assetentry_t *entryTileset = assetLock(
"ui/minogram.dtf", ASSET_LOADER_TYPE_TILESET, NULL
);
errorChain(assetRequireLoaded(entryTexture));
errorChain(assetRequireLoaded(entryTileset));
FONT_DEFAULT.texture = &entryTexture->data.texture;
FONT_DEFAULT.tileset = &entryTileset->data.tileset;
errorOk();
}
errorret_t textDispose(void) {
FONT_DEFAULT.texture = NULL;
FONT_DEFAULT.tileset = NULL;
assetUnlock("ui/minogram.png");
assetUnlock("ui/minogram.dtf");
errorOk();
}
spritebatchsprite_t textGetSprite(
const vec2 pos, const char_t c, const font_t *font
) {
assertNotNull(font, "Font cannot be NULL");
// Change char from ASCII to a tile index.
int32_t tileIndex = (int32_t)(c) - TEXT_CHAR_START;
if(tileIndex < 0 || tileIndex >= font->tileset->tileCount) {
tileIndex = ((int32_t)'@') - TEXT_CHAR_START;
}
assertTrue(
tileIndex >= 0 && tileIndex <= font->tileset->tileCount,
"Character is out of bounds for font tiles"
);
// Create sprite.
vec4 uv;
tilesetTileGetUV(font->tileset, tileIndex, uv);
spritebatchsprite_t sprite;
sprite.min[0] = pos[0];
sprite.min[1] = pos[1];
sprite.min[2] = 0.0f;
sprite.max[0] = pos[0] + font->tileset->tileWidth;
sprite.max[1] = pos[1] + font->tileset->tileHeight;
sprite.max[2] = 0.0f;
sprite.uvMin[0] = uv[0];
sprite.uvMin[1] = uv[1];
sprite.uvMax[0] = uv[2];
sprite.uvMax[1] = uv[3];
return sprite;
}
errorret_t textDraw(
const float_t x,
const float_t y,
const char_t *text,
const color_t color,
font_t *font
) {
assertNotNull(text, "Text cannot be NULL");
spritebatchsprite_t sprite;
shadermaterial_t material = {
.unlit = {
.color = color,
.texture = font->texture
}
};
float_t posX = x;
float_t posY = y;
char_t c;
int32_t i = 0;
while((c = text[i++]) != '\0') {
if(c == '\n') {
posX = x;
posY += font->tileset->tileHeight;
continue;
}
if(c == ' ') {
posX += font->tileset->tileWidth;
continue;
}
sprite = textGetSprite((vec2){posX, posY}, c, font);
errorChain(spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material));
posX += font->tileset->tileWidth;
}
errorOk();
}
void textMeasure(
const char_t *text,
const font_t *font,
int32_t *outWidth,
int32_t *outHeight
) {
assertNotNull(text, "Text cannot be NULL");
assertNotNull(outWidth, "Output width pointer cannot be NULL");
assertNotNull(outHeight, "Output height pointer cannot be NULL");
int32_t width = 0;
int32_t height = font->tileset->tileHeight;
int32_t lineWidth = 0;
char_t c;
int32_t i = 0;
while((c = text[i++]) != '\0') {
if(c == '\n') {
if(lineWidth > width) width = lineWidth;
lineWidth = 0;
height += font->tileset->tileHeight;
continue;
}
lineWidth += font->tileset->tileWidth;
}
if(lineWidth > width) width = lineWidth;
*outWidth = width;
*outHeight = height;
}
-76
View File
@@ -1,76 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "asset/asset.h"
#include "display/text/font.h"
#include "display/spritebatch/spritebatch.h"
#define TEXT_CHAR_START '!'
extern font_t FONT_DEFAULT;
/**
* Initializes the text system.
*
* @return Either an error or success result.
*/
errorret_t textInit(void);
/**
* Disposes of the text system.
*
* @return Either an error or success result.
*/
errorret_t textDispose(void);
/**
* Builds a sprite for a single character at the given position.
*
* @param pos The (x, y) position of the character in screen/world space.
* @param c The character to build a sprite for.
* @param font Font to use for tile lookup.
* @return The populated sprite ready for spriteBatchBuffer.
*/
spritebatchsprite_t textGetSprite(
const vec2 pos,
const char_t c,
const font_t *font
);
/**
* Draws a string of text at the specified position.
*
* @param x The x-coordinate to draw the text at.
* @param y The y-coordinate to draw the text at.
* @param text The null-terminated string of text to draw.
* @param color The color to draw the text in.
* @param font Font to use for rendering.
* @return Either an error or success result.
*/
errorret_t textDraw(
const float_t x,
const float_t y,
const char_t *text,
const color_t color,
font_t *font
);
/**
* Measures the width and height of the given text string when rendered.
*
* @param text The null-terminated string of text to measure.
* @param font Font to use for measurement.
* @param outWidth Pointer to store the measured width in pixels.
* @param outHeight Pointer to store the measured height in pixels.
*/
void textMeasure(
const char_t *text,
const font_t *font,
int32_t *outWidth,
int32_t *outHeight
);
-12
View File
@@ -1,12 +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
tileset.c
texture.c
palette.c
)
-10
View File
@@ -1,10 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "palette.h"
palette_t PALETTES[PALETTE_COUNT];
-19
View File
@@ -1,19 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/color.h"
#define PALETTE_COLOR_COUNT 0xFF
#define PALETTE_COUNT 6
typedef struct {
color_t colors[PALETTE_COLOR_COUNT];
uint8_t count;
} palette_t;
extern palette_t PALETTES[PALETTE_COUNT];
-68
View File
@@ -1,68 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "texture.h"
#include "assert/assert.h"
#include "util/memory.h"
#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,
};
texture_t TEXTURE_TEST;
color_t TEXTURE_TEST_PIXELS[4*4] = {
COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA,
COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK,
COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA,
COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA, COLOR_BLACK,
};
errorret_t textureInit(
texture_t *texture,
const int32_t width,
const int32_t height,
const textureformat_t format,
const texturedata_t data
) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(width > 0 && height > 0, "width/height must be greater than 0");
assertTrue(width == mathNextPowTwo(width), "Width must be a power of 2.");
assertTrue(height == mathNextPowTwo(height), "Height must be a power of 2.");
if(texture->format == TEXTURE_FORMAT_RGBA) {
assertNotNull(data.rgbaColors, "RGBA color data cannot be NULL");
} else if(texture->format == TEXTURE_FORMAT_PALETTE) {
assertNotNull(data.paletted.indices, "Palette indices cannot be NULL");
assertNotNull(data.paletted.palette, "Palette colors cannot be NULL");
assertTrue(
data.paletted.palette->count ==
mathNextPowTwo(data.paletted.palette->count),
"Palette color count must be a power of 2"
);
}
memoryZero(texture, sizeof(texture_t));
texture->width = width;
texture->height = height;
texture->format = format;
errorChain(textureInitPlatform(texture, width, height, format, data));
errorOk();
}
errorret_t textureDispose(texture_t *texture) {
assertNotNull(texture, "Texture cannot be NULL");
errorChain(textureDisposePlatform(texture));
errorOk();
}
-58
View File
@@ -1,58 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/texture/palette.h"
#include "display/texture/textureplatform.h"
#ifndef textureInitPlatform
#error "textureInitPlatform should not be defined."
#endif
#ifndef textureDisposePlatform
#error "textureDisposePlatform should not be defined."
#endif
typedef textureformatplatform_t textureformat_t;
typedef textureplatform_t texture_t;
typedef union texturedata_u {
struct {
uint8_t *indices;
palette_t *palette;
} paletted;
color_t *rgbaColors;
} texturedata_t;
extern texture_t TEXTURE_WHITE;
extern color_t TEXTURE_WHITE_PIXELS[4*4];
extern texture_t TEXTURE_TEST;
extern color_t TEXTURE_TEST_PIXELS[4*4];
/**
* Initializes a texture.
*
* @param texture The texture to initialize.
* @param width The width of the texture.
* @param height The height of the texture.
* @param format The format of the texture (e.g., GL_RGBA, GL_ALPHA).
* @param data The data for the texture, the format changes per format.
*/
errorret_t textureInit(
texture_t *texture,
const int32_t width,
const int32_t height,
const textureformat_t format,
const texturedata_t data
);
/**
* Disposes a texture.
*
* @param texture The texture to dispose.
*/
errorret_t textureDispose(texture_t *texture);
-36
View File
@@ -1,36 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "tileset.h"
#include "assert/assert.h"
#include "util/string.h"
void tilesetTileGetUV(
const tileset_t *tileset,
const uint16_t tileIndex,
vec4 outUV
) {
const uint16_t column = tileIndex % tileset->columns;
const uint16_t row = tileIndex / tileset->columns;
tilesetPositionGetUV(tileset, column, row, outUV);
}
void tilesetPositionGetUV(
const tileset_t *tileset,
const uint16_t column,
const uint16_t row,
vec4 outUV
) {
assertNotNull(tileset, "Tileset cannot be NULL");
assertTrue(column < tileset->columns, "Column index out of bounds");
assertTrue(row < tileset->rows, "Row index out of bounds");
outUV[0] = ((float_t)column) * tileset->uv[0];
outUV[1] = ((float_t)row) * tileset->uv[1];
outUV[2] = outUV[0] + tileset->uv[0];
outUV[3] = outUV[1] + tileset->uv[1];
}
-46
View File
@@ -1,46 +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 tileset_s {
uint16_t tileWidth;
uint16_t tileHeight;
uint16_t tileCount;
uint16_t columns;
uint16_t rows;
vec2 uv;
} tileset_t;
/**
* Gets the UV coordinates for a tile index in the tileset.
*
* @param tileset The tileset to get the UV coordinates from.
* @param tileIndex The index of the tile to get the UV coordinates for.
* @param outUV The output UV coordinates (vec4).
*/
void tilesetTileGetUV(
const tileset_t *tileset,
const uint16_t tileIndex,
vec4 outUV
);
/**
* Gets the UV coordinates for a tile position in the tileset.
*
* @param tileset The tileset to get the UV coordinates from.
* @param column The column of the tile to get the UV coordinates for.
* @param row The row of the tile to get the UV coordinates for.
* @param outUV The output UV coordinates (vec4).
*/
void tilesetPositionGetUV(
const tileset_t *tileset,
const uint16_t column,
const uint16_t row,
vec4 outUV
);
+4 -35
View File
@@ -9,19 +9,12 @@
#include "util/memory.h"
#include "time/time.h"
#include "input/input.h"
#include "locale/localemanager.h"
#include "rpg/rpg.h"
#include "display/display.h"
#include "scene/scene.h"
#include "cutscene/cutscene.h"
#include "asset/asset.h"
#include "ui/ui.h"
#include "ui/uitextbox.h"
#include "assert/assert.h"
#include "network/network.h"
#include "system/system.h"
#include "console/console.h"
#include "save/save.h"
engine_t ENGINE;
@@ -29,48 +22,32 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
assertInit();
memoryZero(&ENGINE, sizeof(engine_t));
ENGINE.running = true;
ENGINE.argc = argc;
ENGINE.argv = argv;
ENGINE.argc = argc;
ENGINE.argv = argv;
ENGINE.version = DUSK_VERSION;
// Init systems. Order is important.
errorChain(systemInit());
timeInit();
consoleInit();
errorChain(inputInit());
errorChain(assetInit());
// errorChain(saveInit());
errorChain(localeManagerInit());
errorChain(displayInit());
errorChain(uiInit());
errorChain(uiTextboxInit());
errorChain(cutsceneInit());
errorChain(rpgInit());
errorChain(networkInit());
errorChain(sceneInit());
consolePrint("Engine initialized");
sceneSet(SCENE_TYPE_OVERWORLD);
sceneSet(SCENE_TYPE_TEST);
errorOk();
}
errorret_t engineUpdate(void) {
// Order here is important.
errorChain(networkUpdate());
timeUpdate();
inputUpdate();
consoleUpdate();
errorChain(rpgUpdate());
uiUpdate();
errorChain(uiTextboxUpdate());
errorChain(cutsceneUpdate());
errorChain(sceneUpdate());
errorChain(assetUpdate());
// Render
errorChain(displayUpdate());
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
errorOk();
}
@@ -80,17 +57,9 @@ void engineExit(void) {
}
errorret_t engineDispose(void) {
uiTextboxDispose();
cutsceneDispose();
errorChain(sceneDispose());
errorChain(networkDispose());
errorChain(rpgDispose());
localeManagerDispose();
uiDispose();
consoleDispose();
errorChain(displayDispose());
// errorChain(saveDispose());
errorChain(assetDispose());
errorOk();
}
+4 -21
View File
@@ -1,40 +1,23 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
// Important to be included first:
#include "display/display.h"
#include "error/error.h"
typedef struct {
bool_t running;
int32_t argc;
bool_t running;
int32_t argc;
const char_t **argv;
const char_t *version;
} engine_t;
extern engine_t ENGINE;
/**
* Initializes the engine.
*
* @param argc The argument count from main().
* @param argv The argument vector from main().
*/
errorret_t engineInit(const int32_t argc, const char_t **argv);
/**
* Updates the engine.
*/
errorret_t engineUpdate(void);
/**
* Shuts down the engine.
*/
void engineExit(void);
errorret_t engineDispose(void);
@@ -3,8 +3,8 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
screen.c
)
ropbuffer.c
render.c
)
+30
View File
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "render/render.h"
void renderClear(color_t color) {
ropclear_t *cmd = ropBufferAlloc(&ROPBUFFER, ROP_CLEAR);
cmd->color = color;
}
void renderSprite(
int16_t x, int16_t y,
int16_t w, int16_t h,
color_t tint
) {
ropsprite_t *cmd = ropBufferAlloc(&ROPBUFFER, ROP_DRAW_SPRITE);
cmd->x = x;
cmd->y = y;
cmd->w = w;
cmd->h = h;
cmd->uvX = 0;
cmd->uvY = 0;
cmd->uvW = 255;
cmd->uvH = 255;
cmd->tint = tint;
}
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "render/rop.h"
#include "render/ropbuffer.h"
void renderClear(color_t color);
void renderSprite(
int16_t x, int16_t y,
int16_t w, int16_t h,
color_t tint
);
+53
View File
@@ -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 "display/color.h"
#define ROP_SIZE 32
typedef enum {
ROP_NOP = 0,
ROP_CLEAR,
ROP_DRAW_SPRITE,
ROP_COUNT
} ropop_t;
#define ROP_FLAG_BLEND ((uint8_t)(1 << 0))
/* 4 bytes, every opcode starts with this */
typedef struct {
uint8_t op;
uint8_t flags;
int16_t depth;
} ropheader_t;
_Static_assert(sizeof(ropheader_t) == 4, "ropheader_t must be 4 bytes");
/* ROP_CLEAR — 32 bytes */
typedef struct {
ropheader_t header; /* 4 */
color_t color; /* 4 */
uint8_t pad[24];/* 24 */
} ropclear_t;
_Static_assert(sizeof(ropclear_t) == ROP_SIZE, "ropclear_t must be ROP_SIZE bytes");
/* ROP_DRAW_SPRITE — 32 bytes, screen-space pixel coordinates */
typedef struct {
ropheader_t header; /* 4 */
int16_t x, y; /* 4 */
int16_t w, h; /* 4 */
uint8_t uvX, uvY; /* 2 */
uint8_t uvW, uvH; /* 2 */
color_t tint; /* 4 */
uint16_t texture; /* 2 handle, 0 = white */
uint16_t palette; /* 2 */
uint8_t pad[8]; /* 8 */
} ropsprite_t;
_Static_assert(sizeof(ropsprite_t) == ROP_SIZE, "ropsprite_t must be ROP_SIZE bytes");
+25
View File
@@ -0,0 +1,25 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "render/ropbuffer.h"
#include "util/memory.h"
#include "assert/assert.h"
ropbuffer_t ROPBUFFER;
void ropBufferReset(ropbuffer_t *buf) {
buf->count = 0;
}
void *ropBufferAlloc(ropbuffer_t *buf, ropop_t op) {
assertTrue(buf->count < ROPBUFFER_MAX_COMMANDS, "ROP buffer is full");
uint8_t *ptr = buf->data + (buf->count * ROP_SIZE);
memoryZero(ptr, ROP_SIZE);
((ropheader_t *)ptr)->op = (uint8_t)op;
buf->count++;
return ptr;
}
+21
View File
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "render/rop.h"
#define ROPBUFFER_MAX_COMMANDS 4096
typedef struct {
uint8_t data[ROPBUFFER_MAX_COMMANDS * ROP_SIZE];
uint32_t count;
} ropbuffer_t;
extern ropbuffer_t ROPBUFFER;
void ropBufferReset(ropbuffer_t *buf);
void *ropBufferAlloc(ropbuffer_t *buf, ropop_t op);
-1
View File
@@ -15,6 +15,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
add_subdirectory(cutscene)
add_subdirectory(entity)
add_subdirectory(overworld)
add_subdirectory(story)
add_subdirectory(item)
+5 -15
View File
@@ -8,26 +8,16 @@
#pragma once
#include "rpg/overworld/tile.h"
#include "worldpos.h"
#include "display/mesh/quad.h"
#include "display/spritebatch/spritebatch.h"
#include "display/color.h"
// #define CHUNK_MESH_COUNT_MAX 3
#define CHUNK_VERTEX_COUNT (QUAD_VERTEX_COUNT * CHUNK_TILE_COUNT)
#define CHUNK_ENTITY_COUNT_MAX 10
typedef struct chunk_s {
chunkpos_t position;
tile_t tiles[CHUNK_TILE_COUNT];
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
uint32_t vertCount;
mesh_t mesh;
color_t testColor;
// uint8_t meshCount;
// meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
// mesh_t meshes[CHUNK_MESH_COUNT_MAX];
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
tile_t tiles[CHUNK_TILE_COUNT];
color_t testColor;
uint32_t vertCount;
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
} chunk_t;
/**
+15 -156
View File
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2025 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
@@ -17,18 +17,6 @@ map_t MAP;
errorret_t mapInit() {
memoryZero(&MAP, sizeof(map_t));
// Setup chunk meshes
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
chunk_t *chunk = &MAP.chunks[i];
errorChain(meshInit(
&chunk->mesh,
MESH_PRIMITIVE_TYPE_TRIANGLES,
CHUNK_VERTEX_COUNT,
chunk->vertices
));
}
// Perform "initial load"
MAP.loaded = true;
int32_t i = 0;
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
@@ -52,61 +40,6 @@ bool_t mapIsLoaded() {
return MAP.loaded;
}
// 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
// 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
// // 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++) {
// chunk_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");
@@ -115,43 +48,31 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
errorOk();
}
// Determine which chunks remain loaded
chunkindex_t chunksRemaining[MAP_CHUNK_COUNT] = {0};
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
chunkindex_t chunksFreed[MAP_CHUNK_COUNT] = {0};
uint32_t remainingCount = 0;
uint32_t freedCount = 0;
uint32_t freedCount = 0;
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
// Will this chunk remain loaded?
chunk_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++) {
chunk_t *chunk = &MAP.chunks[chunksFreed[i]];
mapChunkUnload(chunk);
mapChunkUnload(&MAP.chunks[chunksFreed[i]]);
}
// 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++) {
@@ -159,8 +80,7 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
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++) {
chunk_t *chunk = &MAP.chunks[chunksRemaining[i]];
@@ -169,9 +89,7 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
break;
}
// Need to load this chunk
if(chunkIndex == -1) {
// Find a freed chunk to reuse
chunkIndex = chunksFreed[--freedCount];
chunk_t *chunk = &MAP.chunks[chunkIndex];
chunk->position = newChunkPos;
@@ -183,25 +101,21 @@ errorret_t mapPositionSet(const chunkpos_t newPos) {
}
}
// Update map position
MAP.chunkPosition = newPos;
errorOk();
}
void mapUpdate() {
}
errorret_t mapDispose() {
for(chunkindex_t i = 0; i < MAP_CHUNK_COUNT; i++) {
mapChunkUnload(&MAP.chunks[i]);
errorChain(meshDispose(&MAP.chunks[i].mesh));
}
errorOk();
}
void mapChunkUnload(chunk_t* chunk) {
void mapChunkUnload(chunk_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]];
@@ -210,76 +124,21 @@ void mapChunkUnload(chunk_t* chunk) {
chunk->vertCount = 0;
}
errorret_t mapChunkLoad(chunk_t* chunk) {
errorret_t mapChunkLoad(chunk_t *chunk) {
if(!mapIsLoaded()) errorThrow("No map loaded");
color_t color = COLOR_WHITE;
if(chunk->position.y % 2 == 0) {
if(chunk->position.x % 2 == 0) {
color = COLOR_BLACK;
} else {
color = COLOR_WHITE;
}
color = (chunk->position.x % 2 == 0) ? COLOR_BLACK : COLOR_WHITE;
} else {
if(chunk->position.x % 2 == 0) {
color = COLOR_WHITE;
} else {
color = COLOR_BLACK;
}
color = (chunk->position.x % 2 == 0) ? COLOR_WHITE : COLOR_BLACK;
}
// if(chunk->position.x == 0 && chunk->position.y == 0 && chunk->position.z == 0) {
// color = COLOR_RED;
// }
chunk->testColor = color;
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
chunk->vertCount = 0;
if(chunk->position.z != 0) {
errorOk();
}
// Set Chunk sprites.
vec3 spriteMin = {
chunk->position.x * CHUNK_WIDTH,
chunk->position.y * CHUNK_HEIGHT,
chunk->position.z * CHUNK_DEPTH
};
spritebatchsprite_t sprites[CHUNK_TILE_COUNT];
uint32_t i = 0;
for(uint8_t x = 0; x < CHUNK_WIDTH; x++) {
for(uint8_t y = 0; y < CHUNK_HEIGHT; y++) {
glm_vec3_copy(spriteMin, sprites[i].min);
glm_vec3_add(
sprites[i].min,
(vec3){ x, y, 0 },
sprites[i].min
);
glm_vec3_copy(sprites[i].min, sprites[i].max);
glm_vec3_add(
sprites[i].max,
(vec3){ 1, 1, 0 },
sprites[i].max
);
glm_vec2_copy((vec2){ 0, 0 }, sprites[i].uvMin);
glm_vec2_copy((vec2){ 1, 1 }, sprites[i].uvMax);
i++;
}
}
chunk->vertCount = i * QUAD_VERTEX_COUNT;
spriteBatchBufferToMesh(
sprites,
i,
chunk->vertices,
chunk->vertCount
);
errorChain(meshFlush(&chunk->mesh, 0, chunk->vertCount));
errorOk();
}
@@ -304,7 +163,7 @@ chunkindex_t mapGetChunkIndexAt(const chunkpos_t position) {
return chunkPosToIndex(&relPos);
}
chunk_t* mapGetChunk(const uint8_t index) {
chunk_t *mapGetChunk(const uint8_t index) {
if(index >= MAP_CHUNK_COUNT) return NULL;
if(!mapIsLoaded()) return NULL;
return MAP.chunkOrder[index];
@@ -322,4 +181,4 @@ tile_t mapGetTile(const worldpos_t position) {
assertNotNull(chunk, "Chunk pointer cannot be NULL");
chunktileindex_t tileIndex = worldPosToChunkTileIndex(&position);
return chunk->tiles[tileIndex];
}
}
+1
View File
@@ -6,6 +6,7 @@
*/
#pragma once
#include "error/error.h"
#include "rpg/overworld/chunk.h"
#define MAP_FILE_PATH_MAX 128
+2 -1
View File
@@ -10,4 +10,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
)
# Subdirs
add_subdirectory(overworld)
add_subdirectory(overworld)
add_subdirectory(test)
+6 -181
View File
@@ -1,203 +1,28 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scene/scene.h"
#include "console/console.h"
#include "assert/assert.h"
#include "display/shader/shader.h"
#include "display/screen/screen.h"
#include "display/shader/shaderunlit.h"
#include "display/spritebatch/spritebatch.h"
#include "rpg/overworld/map.h"
#include "rpg/entity/entity.h"
#include "rpg/rpgcamera.h"
#include "util/math.h"
#include "scene/overworld/sceneoverworld.h"
errorret_t sceneOverworldInit(scenedata_t *sceneData) {
assertNotNull(sceneData, "Scene data cannot be null");
(void)sceneData;
errorOk();
}
errorret_t sceneOverworldUpdate(scenedata_t *sceneData) {
assertNotNull(sceneData, "Scene data cannot be null");
(void)sceneData;
errorOk();
}
errorret_t sceneOverworldRender(scenedata_t *sceneData) {
assertNotNull(sceneData, "Scene data cannot be null");
mat4 proj, model, eye;
errorChain(shaderBind(&SHADER_UNLIT));
// Model
glm_mat4_identity(model);
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
// Camera projection
float_t fov = glm_rad(45.0f);
glm_perspective(
fov,
(float_t)SCREEN.width / (float_t)SCREEN.height,
0.1f,
100.0f,
proj
);
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
// Camera view
{
vec3 target = { 0.5f, 0.5f, 0.5f };
if(RPG_CAMERA.mode == RPG_CAMERA_MODE_FOLLOW_ENTITY) {
entity_t *followed = &ENTITIES[RPG_CAMERA.followEntity.followEntityId];
if(followed->type != ENTITY_TYPE_NULL) {
float_t walkT = followed->animation == ENTITY_ANIM_WALK
? fixedToFloat(
fixedDiv(followed->animTime, ENTITY_ANIM_WALK_DURATION)
)
: 0.0f;
target[0] = mathLerp(
fixedToFloat(followed->position[0]),
fixedToFloat(followed->lastPosition[0]),
walkT
) + 0.5f;
target[1] = mathLerp(
fixedToFloat(followed->position[1]),
fixedToFloat(followed->lastPosition[1]),
walkT
) + 0.5f;
target[2] = mathLerp(
fixedToFloat(followed->position[2]),
fixedToFloat(followed->lastPosition[2]),
walkT
) + 0.5f;
}
} else {
worldpos_t camPos = rpgCameraGetPosition();
target[0] = (float_t)camPos.x + 0.5f;
target[1] = (float_t)camPos.y + 0.5f;
target[2] = (float_t)camPos.z + 0.5f;
}
float_t pixelsPerUnit = 16.0f;
float_t worldH = (float_t)SCREEN.height / pixelsPerUnit;
float_t eyeZ = (worldH * 0.5f) / tanf(fov * 0.5f);
float_t offset = -16.0f;
glm_lookat(
(vec3){ target[0], target[1] + offset, target[2] + eyeZ },
target,
(vec3){ 0, 1, 0 },
eye
);
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, eye));
}
// Chunks
{
shadermaterial_t chunkMaterial = {
.unlit = {
.color = COLOR_WHITE,
.texture = NULL
}
};
uint32_t i = 0;
for(uint8_t x = 0; x < MAP_CHUNK_WIDTH; x++) {
for(uint8_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
for(uint8_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
chunk_t *chunk = &MAP.chunks[i];
if(chunk->vertCount == 0) {
i++;
continue;
}
chunkMaterial.unlit.color = chunk->testColor;
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
errorChain(meshDraw(&chunk->mesh, 0, chunk->vertCount));
i++;
}
}
}
}
// Entities
{
uint8_t spriteCount = 0;
spritebatchsprite_t sprites[ENTITY_COUNT];
for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
entity_t *ent = &ENTITIES[i];
if(ent->type == ENTITY_TYPE_NULL) continue;
float_t walkT = ent->animation == ENTITY_ANIM_WALK
? fixedToFloat(fixedDiv(ent->animTime, ENTITY_ANIM_WALK_DURATION))
: 0.0f;
vec3 position = {
mathLerp(
fixedToFloat(ent->position[0]),
fixedToFloat(ent->lastPosition[0]),
walkT
),
mathLerp(
fixedToFloat(ent->position[1]),
fixedToFloat(ent->lastPosition[1]),
walkT
),
mathLerp(
fixedToFloat(ent->position[2]),
fixedToFloat(ent->lastPosition[2]),
walkT
) + 0.01f
};
glm_vec3_copy(position, sprites[spriteCount].min);
glm_vec3_copy(position, sprites[spriteCount].max);
glm_vec3_add(
sprites[spriteCount].max,
(vec3){ 1, 1, 0 },
sprites[spriteCount].max
);
glm_vec2_copy((vec2){ 0, 0 }, sprites[spriteCount].uvMin);
glm_vec2_copy((vec2){ 1, 1 }, sprites[spriteCount].uvMax);
spriteCount++;
}
if(spriteCount) {
shadermaterial_t material = {
.unlit = {
.color = COLOR_CYAN,
.texture = NULL
}
};
// material.unlit.texture = &TEXTURE_TEST;
spriteBatchBuffer(sprites, spriteCount, &SHADER_UNLIT, material);
spriteBatchFlush();
}
}
(void)sceneData;
errorOk();
}
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
assertNotNull(sceneData, "Scene data cannot be null");
(void)sceneData;
errorOk();
}
+6 -42
View File
@@ -7,12 +7,7 @@
#include "assert/assert.h"
#include "util/memory.h"
#include "time/time.h"
#include "display/screen/screen.h"
#include "display/shader/shaderunlit.h"
#include "display/display.h"
#include "ui/ui.h"
#include "asset/asset.h"
#include "asset/loader/assetloader.h"
#include "console/console.h"
scene_t SCENE;
@@ -23,7 +18,6 @@ errorret_t sceneInit(void) {
}
errorret_t sceneUpdate(void) {
// Handle scene change.
if(SCENE.next != SCENE_TYPE_NULL) {
if(
SCENE.current != SCENE_TYPE_NULL &&
@@ -33,7 +27,7 @@ errorret_t sceneUpdate(void) {
}
SCENE.current = SCENE.next;
SCENE.next = SCENE_TYPE_NULL;
SCENE.next = SCENE_TYPE_NULL;
if(
SCENE.current != SCENE_TYPE_NULL &&
@@ -43,11 +37,11 @@ errorret_t sceneUpdate(void) {
}
}
#if DUSK_TIME_DYNAMIC
if(TIME.dynamicUpdate) {
errorOk();
}
#endif
#if DUSK_TIME_DYNAMIC
if(TIME.dynamicUpdate) {
errorOk();
}
#endif
if(
SCENE.current != SCENE_TYPE_NULL &&
@@ -60,41 +54,12 @@ errorret_t sceneUpdate(void) {
}
errorret_t sceneRender(void) {
// Scene rendering
if(
SCENE.current != SCENE_TYPE_NULL &&
SCENE_TYPES[SCENE.current].render != NULL
) {
errorChain(SCENE_TYPES[SCENE.current].render(&SCENE.data));
}
// UI Rendering
mat4 proj, view, ident;
glm_mat4_identity(ident);
errorChain(shaderBind(&SHADER_UNLIT));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, ident));
glm_ortho(
0.0f, SCREEN.width,
SCREEN.height, 0.0f,
0.1f, 100.0f,
proj
);
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
glm_lookat(
(vec3){ 0.0f, 0.0f, 1.0f },
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f },
view
);
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
errorChain(displaySetState((displaystate_t){
.flags = DISPLAY_STATE_FLAG_BLEND
}));
errorChain(uiRender());
errorOk();
}
@@ -110,6 +75,5 @@ errorret_t sceneDispose(void) {
) {
errorChain(SCENE_TYPES[SCENE.current].dispose(&SCENE.data));
}
errorOk();
}
+11 -5
View File
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
@@ -11,10 +11,16 @@ scenecallbacks_t SCENE_TYPES[SCENE_TYPE_COUNT] = {
[SCENE_TYPE_NULL] = { 0 },
[SCENE_TYPE_OVERWORLD] = {
.init = sceneOverworldInit,
.update = sceneOverworldUpdate,
.render = sceneOverworldRender,
.init = sceneOverworldInit,
.update = sceneOverworldUpdate,
.render = sceneOverworldRender,
.dispose = sceneOverworldDispose
},
};
[SCENE_TYPE_TEST] = {
.init = sceneTestInit,
.update = sceneTestUpdate,
.render = sceneTestRender,
.dispose = sceneTestDispose
},
};
+3 -2
View File
@@ -8,9 +8,11 @@
#pragma once
#include "scene/scenebase.h"
#include "scene/overworld/sceneoverworld.h"
#include "scene/test/scenetest.h"
typedef union scenedata_u {
sceneoverworld_t overworld;
scenetest_t test;
} scenedata_t;
typedef errorret_t (*scenecallback_t)(scenedata_t *);
@@ -24,9 +26,8 @@ typedef struct {
typedef enum {
SCENE_TYPE_NULL,
SCENE_TYPE_OVERWORLD,
SCENE_TYPE_TEST,
SCENE_TYPE_COUNT
} scenetype_t;
@@ -3,8 +3,7 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
text.c
)
scenetest.c
)
+32
View File
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scene/test/scenetest.h"
#include "render/render.h"
#include "display/color.h"
errorret_t sceneTestInit(scenedata_t *data) {
(void)data;
errorOk();
}
errorret_t sceneTestUpdate(scenedata_t *data) {
(void)data;
errorOk();
}
errorret_t sceneTestRender(scenedata_t *data) {
(void)data;
renderClear(color(32, 32, 48, 255));
renderSprite(100, 100, 32, 32, COLOR_WHITE);
errorOk();
}
errorret_t sceneTestDispose(scenedata_t *data) {
(void)data;
errorOk();
}
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "scene/scenebase.h"
typedef struct {
int32_t unused;
} scenetest_t;
errorret_t sceneTestInit(scenedata_t *data);
errorret_t sceneTestUpdate(scenedata_t *data);
errorret_t sceneTestRender(scenedata_t *data);
errorret_t sceneTestDispose(scenedata_t *data);
+1 -12
View File
@@ -3,15 +3,4 @@
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
ui.c
uifps.c
uielement.c
uiframe.c
uifullbox.c
uiloading.c
uitextbox.c
uiplayerpos.c
)
# Display-dependent UI sources are temporarily disabled pending rop-based rewrite.
+2 -4
View File
@@ -3,12 +3,10 @@
# 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(display)
add_subdirectory(error)
add_subdirectory(error)
add_subdirectory(render)
-16
View File
@@ -1,16 +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
displaygl.c
)
# Subdirs
add_subdirectory(framebuffer)
add_subdirectory(texture)
add_subdirectory(mesh)
add_subdirectory(shader)
-22
View File
@@ -1,22 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "displaygl.h"
errorret_t displayOpenGLInit(void) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
errorChain(errorGLCheck());
#if DUSK_OPENGL_LEGACY
glDisable(GL_LIGHTING);// PSP defaults this on?
errorChain(errorGLCheck());
glShadeModel(GL_SMOOTH); // Fixes color on PSP?
errorChain(errorGLCheck());
#endif
errorOk();
}
-16
View File
@@ -1,16 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/errorgl.h"
/**
* Initializes the OpenGL specific contexts for rendering.
*
* @return An errorret_t indicating success or failure of the initialization.
*/
errorret_t displayOpenGLInit(void);
@@ -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
framebuffergl.c
)
@@ -1,149 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/display.h"
#include "display/framebuffer/framebuffer.h"
#include "assert/assertgl.h"
#include "util/memory.h"
errorret_t frameBufferGLInitBackBuffer(void) {
errorOk();
}
uint32_t frameBufferGLGetWidth(const framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
return 0;
}
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
return windowWidth;
#else
return DUSK_DISPLAY_WIDTH;
#endif
}
return framebuffer->texture.width;
}
uint32_t frameBufferGLGetHeight(const framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
return 0;
}
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
return windowHeight;
#else
return DUSK_DISPLAY_HEIGHT;
#endif
}
return framebuffer->texture.height;
}
errorret_t frameBufferGLBind(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
} else {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
}
glViewport(
0, 0,
frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer)
);
#else
glViewport(
0, 0,
DUSK_DISPLAY_WIDTH, DUSK_DISPLAY_HEIGHT
);
#endif
errorChain(errorGLCheck());
errorOk();
}
void frameBufferGLClear(const uint8_t flags, const color_t color) {
GLbitfield glFlags = 0;
if(flags & FRAMEBUFFER_CLEAR_COLOR) {
glFlags |= GL_COLOR_BUFFER_BIT;
glClearColor(
color.r / 255.0f,
color.g / 255.0f,
color.b / 255.0f,
color.a / 255.0f
);
assertNoGLError("Failed to set clear color");
}
if(flags & FRAMEBUFFER_CLEAR_DEPTH) {
glFlags |= GL_DEPTH_BUFFER_BIT;
}
glClear(glFlags);
assertNoGLError("Failed to clear framebuffer");
}
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
errorret_t frameBufferGLInit(
framebuffer_t *fb,
const uint32_t width,
const uint32_t height
) {
assertNotNull(fb, "Framebuffer cannot be NULL");
assertTrue(width > 0 && height > 0, "W/H must be greater than 0");
memoryZero(fb, sizeof(framebuffer_t));
textureInit(&fb->texture, width, height, TEXTURE_FORMAT_RGBA,
(texturedata_t){ .rgbaColors = NULL }
);
errorChain(errorGLCheck());
glGenFramebuffersEXT(1, &fb->id);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb->id);
errorChain(errorGLCheck());
glFramebufferTexture2DEXT(
GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, fb->texture.id, 0
);
errorChain(errorGLCheck());
if(
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE_EXT
) {
assertUnreachable("Framebuffer is not complete");
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
errorChain(errorGLCheck());
errorOk();
}
errorret_t frameBufferGLDispose(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
assertUnreachable("Cannot dispose of backbuffer");
}
errorChain(textureDispose(&framebuffer->texture));
glDeleteFramebuffersEXT(1, &framebuffer->id);
errorChain(errorGLCheck());
errorOk();
}
#endif
@@ -1,78 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/texture/texture.h"
#include "error/errorgl.h"
typedef struct {
GLuint id;
texture_t texture;
} framebuffergl_t;
/**
* Initializes the backbuffer framebuffer. (OpenGL implementation).
*/
errorret_t frameBufferGLInitBackBuffer(void);
/**
* Gets the height of the framebuffer. (OpenGL implementation).
*
* @param framebuffer The framebuffer to get the height of.
* @return The height of the framebuffer, or 0 if the framebuffer is NULL.
*/
uint32_t frameBufferGLGetWidth(const framebuffergl_t *framebuffer);
/**
* Initializes an OpenGL style framebuffer.
*
* @param fb The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
* @return Either error or not.
*/
uint32_t frameBufferGLGetHeight(const framebuffergl_t *framebuffer);
/**
* Gets the width of the framebuffer. (OpenGL implementation).
*
* @param framebuffer The framebuffer to get the width of.
* @return The width of the framebuffer, or 0 if the framebuffer is NULL.
*/
errorret_t frameBufferGLBind(framebuffergl_t *framebuffer);
/**
* Clears the framebuffer with the specified flags and color.
*
* @param flags The clear flags.
* @param color The clear color.
*/
void frameBufferGLClear(const uint8_t flags, const color_t color);
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
/**
* Initializes an OpenGL style framebuffer.
*
* @param fb The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
* @return Either error or not.
*/
errorret_t frameBufferGLInit(
framebuffergl_t *fb,
const uint32_t width,
const uint32_t height
);
/**
* Disposes of the framebuffer. Will also be used for request disposing of the
* backbuffer.
*
* @param framebuffer The framebuffer to dispose of.
*/
errorret_t frameBufferGLDispose(framebuffergl_t *framebuffer);
#endif
@@ -1,21 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/framebuffer/framebuffergl.h"
typedef framebuffergl_t framebufferplatform_t;
#define frameBufferPlatformInitBackBuffer frameBufferGLInitBackBuffer
#define frameBufferPlatformGetWidth frameBufferGLGetWidth
#define frameBufferPlatformGetHeight frameBufferGLGetHeight
#define frameBufferPlatformBind frameBufferGLBind
#define frameBufferPlatformClear frameBufferGLClear
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
#define frameBufferPlatformInit frameBufferGLInit
#define frameBufferPlatformDispose frameBufferGLDispose
#endif
-10
View File
@@ -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
meshgl.c
)
-196
View File
@@ -1,196 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/mesh/mesh.h"
#include "assert/assertgl.h"
#include "error/errorgl.h"
#include "display/shader/shadergl.h"
errorret_t meshInitGL(
meshgl_t *mesh,
const meshprimitivetypegl_t primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertNotNull(vertices, "Vertices cannot be NULL");
assertTrue(vertexCount > 0, "Vertex count must be greater than 0");
mesh->primitiveType = primitiveType;
mesh->vertexCount = vertexCount;
mesh->vertices = vertices;
#ifdef DUSK_OPENGL_LEGACY
// Nothing needed.
#if MESH_ENABLE_COLOR
glEnableClientState(GL_COLOR_ARRAY);
errorChain(errorGLCheck());
#endif
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
errorChain(errorGLCheck());
glEnableClientState(GL_VERTEX_ARRAY);
errorChain(errorGLCheck());
#else
// Generate Vertex Buffer Object
glGenBuffers(1, &mesh->vboId);
errorChain(errorGLCheck());
glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId);
errorChain(errorGLCheck());
glBufferData(
GL_ARRAY_BUFFER,
vertexCount * sizeof(meshvertex_t),
vertices,
GL_DYNAMIC_DRAW
);
errorChain(errorGLCheck());
// Generate Vertex Array Object
glGenVertexArrays(1, &mesh->vaoId);
errorChain(errorGLCheck());
glBindVertexArray(mesh->vaoId);
errorChain(errorGLCheck());
glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId);
errorChain(errorGLCheck());
// Set up vertex attribute pointers
glVertexAttribPointer(
0,
MESH_VERTEX_POS_SIZE,
GL_FLOAT,
GL_FALSE,
sizeof(meshvertex_t),
(const GLvoid*)offsetof(meshvertex_t, pos)
);
errorChain(errorGLCheck());
glEnableVertexAttribArray(0);
errorChain(errorGLCheck());
glVertexAttribPointer(
1,
MESH_VERTEX_UV_SIZE,
GL_FLOAT,
GL_FALSE,
sizeof(meshvertex_t),
(const GLvoid*)offsetof(meshvertex_t, uv)
);
errorChain(errorGLCheck());
glEnableVertexAttribArray(1);
errorChain(errorGLCheck());
#if MESH_ENABLE_COLOR
glVertexAttribPointer(
2,
sizeof(color_t) / sizeof(GLubyte),
GL_UNSIGNED_BYTE,
GL_TRUE,
sizeof(meshvertex_t),
(const GLvoid*)offsetof(meshvertex_t, color)
);
errorChain(errorGLCheck());
glEnableVertexAttribArray(2);
errorChain(errorGLCheck());
#endif
// Unbind VAO and VBO to prevent accidental modification
glBindBuffer(GL_ARRAY_BUFFER, 0);
errorChain(errorGLCheck());
glBindVertexArray(0);
errorChain(errorGLCheck());
#endif
errorOk();
}
errorret_t meshFlushGL(
meshgl_t *mesh,
const int32_t vertOffset,
const int32_t vertCount
) {
#ifdef DUSK_OPENGL_LEGACY
// Nothing doing, we use the glClientState stuff.
#else
glBindBuffer(GL_ARRAY_BUFFER, mesh->vboId);
errorChain(errorGLCheck());
glBufferData(
GL_ARRAY_BUFFER,
mesh->vertexCount * sizeof(meshvertex_t),
mesh->vertices,
// vertCount * sizeof(meshvertex_t),
// &mesh->vertices[vertOffset],
GL_DYNAMIC_DRAW
);
errorChain(errorGLCheck());
#endif
errorOk();
}
errorret_t meshDrawGL(
const meshgl_t *mesh,
const int32_t offset,
const int32_t count
) {
#ifdef DUSK_OPENGL_LEGACY
// Legacy pointer style rendering
const GLsizei stride = sizeof(meshvertex_t);
#if MESH_ENABLE_COLOR
glColorPointer(
sizeof(color4b_t),
GL_UNSIGNED_BYTE,
stride,
(const GLvoid*)&mesh->vertices[offset].color
);
#endif
glTexCoordPointer(
MESH_VERTEX_UV_SIZE,
GL_FLOAT,
stride,
(const GLvoid*)&mesh->vertices[offset].uv[0]
);
glVertexPointer(
MESH_VERTEX_POS_SIZE,
GL_FLOAT,
stride,
(const GLvoid*)&mesh->vertices[offset].pos[0]
);
// Shader may have model matrix here
errorChain(shaderLegacyMatrixUpdate());
glDrawArrays(mesh->primitiveType, 0, count);
errorChain(errorGLCheck());
#else
// Modern VAO/VBO rendering
glBindVertexArray(mesh->vaoId);
errorChain(errorGLCheck());
glDrawArrays(mesh->primitiveType, offset, count);
errorChain(errorGLCheck());
glBindVertexArray(0);
errorChain(errorGLCheck());
#endif
errorOk();
}
int32_t meshGetVertexCountGL(const meshgl_t *mesh) {
return mesh->vertexCount;
}
errorret_t meshDisposeGL(meshgl_t *mesh) {
#ifdef DUSK_OPENGL_LEGACY
// No dynamic resources to free for this mesh implementation
#else
glDeleteBuffers(1, &mesh->vboId);
errorChain(errorGLCheck());
glDeleteVertexArrays(1, &mesh->vaoId);
errorChain(errorGLCheck());
#endif
errorOk();
}
-89
View File
@@ -1,89 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/mesh/meshvertex.h"
typedef enum {
MESH_PRIMITIVE_TYPE_TRIANGLES = GL_TRIANGLES,
MESH_PRIMITIVE_TYPE_LINES = GL_LINES,
MESH_PRIMITIVE_TYPE_POINTS = GL_POINTS,
} meshprimitivetypegl_t;
typedef struct {
int32_t vertexCount;
meshprimitivetypegl_t primitiveType;
const meshvertex_t *vertices;
#ifdef DUSK_OPENGL_LEGACY
// Nothing needed
#else
GLuint vaoId;
GLuint vboId;
#endif
} meshgl_t;
/**
* Initializes a mesh for OpenGL.
*
* @param mesh The mesh to initialize.
* @param primitiveType The OpenGL primitive type (e.g., GL_TRIANGLES).
* @param vertexCount The number of vertices in the mesh.
* @param vertices The vertex data for the mesh.
* @return An errorret_t indicating success or failure.
*/
errorret_t meshInitGL(
meshgl_t *mesh,
const meshprimitivetypegl_t primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
);
/**
* Flushes the vertices (stored in memory) to the GPU.
*
* @param mesh Mesh to flush vertices for.
* @param vertOffset First vertice index to flush.
* @param vertCount Count of vertices to flush.
* @return Error state.
*/
errorret_t meshFlushGL(
meshgl_t *mesh,
const int32_t vertOffset,
const int32_t vertCount
);
/**
* Draws a mesh using OpenGL.
*
* @param mesh The mesh to draw.
* @param vertexOffset The offset in the vertex array to start drawing from.
* @param vertexCount The number of vertices to draw. If -1, draws all vertices.
* @return An errorret_t indicating success or failure.
*/
errorret_t meshDrawGL(
const meshgl_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
);
/**
* Gets the vertex count of a mesh used for OpenGL.
*
* @param mesh The mesh to get the vertex count from.
* @return The vertex count of the mesh.
*/
int32_t meshGetVertexCountGL(const meshgl_t *mesh);
/**
* Disposes a mesh used for OpenGL.
*
* @param mesh The mesh to dispose.
* @return An errorret_t indicating success or failure.
*/
errorret_t meshDisposeGL(meshgl_t *mesh);
-18
View File
@@ -1,18 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "meshgl.h"
typedef meshprimitivetypegl_t meshprimitivetypeplatform_t;
typedef meshgl_t meshplatform_t;
#define meshInitPlatform meshInitGL
#define meshFlushPlatform meshFlushGL
#define meshDrawPlatform meshDrawGL
#define meshGetVertexCountPlatform meshGetVertexCountGL
#define meshDisposePlatform meshDisposeGL
-11
View File
@@ -1,11 +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
shadergl.c
shaderunlitgl.c
)
-415
View File
@@ -1,415 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "shadergl.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assertgl.h"
#include "display/shader/shaderunlit.h"
#ifdef DUSK_OPENGL_LEGACY
shaderlegacygl_t SHADER_LEGACY = { 0 };
#endif
errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) {
assertNotNull(shader, "Shader cannot be null");
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);
glm_mat4_identity(shader->model);
SHADER_LEGACY.boundShader = NULL;
errorOk();
#else
assertNotNull(def->vert, "Vertex shader source cannot be null");
assertNotNull(def->frag, "Fragment shader source cannot be null");
// Create vertex shader
shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
errorret_t err = errorGLCheck();
errorChain(err);
glShaderSource(shader->vertexShaderId, 1, &def->vert, NULL);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteShader(shader->vertexShaderId);
errorChain(err);
}
glCompileShader(shader->vertexShaderId);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteShader(shader->vertexShaderId);
errorChain(err);
}
GLint ok = 0;
glGetShaderiv(shader->vertexShaderId, GL_COMPILE_STATUS, &ok);
if(!ok) {
GLchar log[1024];
glGetShaderInfoLog(shader->vertexShaderId, sizeof(log), NULL, log);
glDeleteShader(shader->vertexShaderId);
errorThrow("Vertex shader compilation failed: %s", log);
}
// Create fragment shader
shader->fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteShader(shader->vertexShaderId);
errorChain(err);
}
glShaderSource(shader->fragmentShaderId, 1, &def->frag, NULL);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorChain(err);
}
glCompileShader(shader->fragmentShaderId);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorChain(err);
}
glGetShaderiv(shader->fragmentShaderId, GL_COMPILE_STATUS, &ok);
if(!ok) {
GLchar log[1024];
glGetShaderInfoLog(shader->fragmentShaderId, sizeof(log), NULL, log);
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorThrow("Fragment shader compilation failed: %s", log);
}
// Create shader program
shader->shaderProgramId = glCreateProgram();
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorChain(err);
}
glAttachShader(shader->shaderProgramId, shader->vertexShaderId);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteProgram(shader->shaderProgramId);
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorChain(err);
}
glAttachShader(shader->shaderProgramId, shader->fragmentShaderId);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteProgram(shader->shaderProgramId);
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorChain(err);
}
glLinkProgram(shader->shaderProgramId);
err = errorGLCheck();
if(errorIsNotOk(err)) {
glDeleteProgram(shader->shaderProgramId);
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorChain(err);
}
ok = 0;
glGetProgramiv(shader->shaderProgramId, GL_LINK_STATUS, &ok);
if(!ok) {
GLchar log[1024];
glGetProgramInfoLog(shader->shaderProgramId, sizeof(log), NULL, log);
glDeleteProgram(shader->shaderProgramId);
glDeleteShader(shader->vertexShaderId);
glDeleteShader(shader->fragmentShaderId);
errorThrow("Shader program linking failed: %s", log);
}
#endif
errorOk();
}
errorret_t shaderParamGetLocationGL(
shadergl_t *shader,
const char_t *name,
GLint *location
) {
assertNotNull(shader, "Shader cannot be null");
assertStrLenMin(name, 1, "Uniform name cannot be empty");
assertNotNull(location, "Location cannot be null");
#ifdef DUSK_OPENGL_LEGACY
assertUnreachable("Cannot get uniform locations on legacy opengl.");
#else
*location = glGetUniformLocation(shader->shaderProgramId, name);
errorChain(errorGLCheck());
if(*location == -1) {
errorThrow("Uniform '%s' not found in shader.", name);
}
#endif
errorOk();
}
errorret_t shaderSetMatrixGL(
shadergl_t *shader,
const char_t *name,
mat4 mat
) {
assertNotNull(shader, "Shader cannot be null");
assertStrLenMin(name, 1, "Uniform name cannot be empty");
assertNotNull(mat, "Matrix data cannot be null");
#ifdef DUSK_OPENGL_LEGACY
assertTrue(
SHADER_LEGACY.boundShader == shader,
"Shader must be bound to set legacy matrices."
);
// Use unaligned copy to safely handle possibly unaligned input matrices
if(stringCompare(name, SHADER_UNLIT_PROJECTION) == 0) {
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_PROJ;
glm_mat4_ucopy(mat, shader->proj);
} else if(stringCompare(name, SHADER_UNLIT_VIEW) == 0) {
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_VIEW;
glm_mat4_ucopy(mat, shader->view);
} else if(stringCompare(name, SHADER_UNLIT_MODEL) == 0) {
SHADER_LEGACY.dirty |= SHADER_LEGACY_DIRTY_MODEL;
glm_mat4_ucopy(mat, shader->model);
} else {
assertUnreachable("Cannot use a custom matrix on legacy opengl.");
}
#else
GLint location;
errorChain(shaderParamGetLocationGL(shader, name, &location));
glUniformMatrix4fv(location, 1, GL_FALSE, (const GLfloat *)mat);
errorChain(errorGLCheck());
#endif
errorOk();
}
errorret_t shaderSetTextureGL(
shadergl_t *shader,
const char_t *name,
texture_t *texture
) {
assertNotNull(shader, "Shader cannot be null");
assertStrLenMin(name, 1, "Uniform name cannot be empty");
#ifdef DUSK_OPENGL_LEGACY
assertStringEqual(
name,
SHADER_UNLIT_TEXTURE,
"Only one texture supported in legacy opengl."
);
// glActiveTexture(GL_TEXTURE0);
errorChain(errorGLCheck());
if(texture == NULL) {
glDisable(GL_TEXTURE_2D);
errorChain(errorGLCheck());
errorOk();
}
glEnable(GL_TEXTURE_2D);
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
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());
glColor4f(
(float_t)color.r / 255.0f,
(float_t)color.g / 255.0f,
(float_t)color.b / 255.0f,
(float_t)color.a / 255.0f
);
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();
}
errorret_t shaderBindGL(shadergl_t *shader) {
#ifdef DUSK_OPENGL_LEGACY
assertNotNull(shader, "Cannot bind a null shader.");
SHADER_LEGACY.boundShader = shader;
SHADER_LEGACY.dirty = (
SHADER_LEGACY_DIRTY_MODEL |
SHADER_LEGACY_DIRTY_PROJ |
SHADER_LEGACY_DIRTY_VIEW
);
#else
assertNotNull(shader, "Shader cannot be null");
glUseProgram(shader->shaderProgramId);
errorChain(errorGLCheck());
#endif
errorOk();
}
errorret_t shaderDisposeGL(shadergl_t *shader) {
assertNotNull(shader, "Shader cannot be null");
#ifdef DUSK_OPENGL_LEGACY
SHADER_LEGACY.boundShader = NULL;
#else
if(shader->shaderProgramId != 0) {
glDeleteProgram(shader->shaderProgramId);
}
if(shader->vertexShaderId != 0) {
glDeleteShader(shader->vertexShaderId);
}
if(shader->fragmentShaderId != 0) {
glDeleteShader(shader->fragmentShaderId);
}
assertNoGLError("Failed disposing shader");
#endif
memoryZero(shader, sizeof(shadergl_t));
errorOk();
}
#ifdef DUSK_OPENGL_LEGACY
errorret_t shaderLegacyMatrixUpdate() {
assertNotNull(SHADER_LEGACY.boundShader, "No shader is currently bound.");
if((SHADER_LEGACY.dirty & SHADER_LEGACY_DIRTY_PROJ) != 0) {
glMatrixMode(GL_PROJECTION);
errorChain(errorGLCheck());
glLoadIdentity();
errorChain(errorGLCheck());
glMultMatrixf((const GLfloat *)SHADER_LEGACY.boundShader->proj);
errorChain(errorGLCheck());
}
if(
(SHADER_LEGACY.dirty &
(SHADER_LEGACY_DIRTY_VIEW | SHADER_LEGACY_DIRTY_MODEL)) != 0
) {
glMatrixMode(GL_MODELVIEW);
errorChain(errorGLCheck());
mat4 viewModel;
glm_mat4_mul(
SHADER_LEGACY.boundShader->view,
SHADER_LEGACY.boundShader->model,
viewModel
);
glLoadMatrixf((const GLfloat *)viewModel);
errorChain(errorGLCheck());
}
SHADER_LEGACY.dirty = 0;
errorOk();
}
#endif
-148
View File
@@ -1,148 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/errorgl.h"
#include "display/texture/texture.h"
typedef struct shadergl_s shadergl_t;
typedef union shadermaterial_u shadermaterial_t;
typedef errorret_t (*shadersettexturefn_t)(
shadergl_t *,
const char_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;
mat4 model;
#else
GLuint shaderProgramId;
GLuint vertexShaderId;
GLuint fragmentShaderId;
#endif
} shadergl_t;
#if DUSK_OPENGL_LEGACY
typedef struct {
shadergl_t *boundShader;
uint_fast8_t dirty;
} shaderlegacygl_t;
extern shaderlegacygl_t SHADER_LEGACY;
#define SHADER_LEGACY_DIRTY_PROJ (1 << 0)
#define SHADER_LEGACY_DIRTY_VIEW (1 << 1)
#define SHADER_LEGACY_DIRTY_MODEL (1 << 2)
#endif
/**
* Initializes a shader.
*
* @param shader The shader to initialize.
* @param def The definition of the shader to initialize with.
* @return An errorret_t indicating success or failure.
*/
errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def);
/**
* Binds a shader for use in rendering.
*
* @param shader The shader to bind.
* @return An errorret_t indicating success or failure.
*/
errorret_t shaderBindGL(shadergl_t *shader);
/**
* Retrieves the location of a shader uniform parameter.
*
* @param shader The shader to query.
* @param name The name of the uniform parameter.
* @param location Output parameter to receive the location of the uniform.
* @return An errorret_t indicating success or failure.
*/
errorret_t shaderParamGetLocationGL(
shadergl_t *shader,
const char_t *name,
GLint *location
);
/**
* Sets a mat4 uniform parameter in the shader.
*
* @param shader The shader to update.
* @param name The name of the uniform parameter.
* @param mat The 4x4 matrix data to set.
* @return An errorret_t indicating success or failure.
*/
errorret_t shaderSetMatrixGL(
shadergl_t *shader,
const char_t *name,
mat4 matrix
);
/**
* Sets a color uniform parameter in the shader.
*
* @param shader The shader to update.
* @param name The name of the uniform parameter.
* @param color The color data to set.
* @return An errorret_t indicating success or failure.
*/
errorret_t shaderSetTextureGL(
shadergl_t *shader,
const char_t *name,
texture_t *texture
);
/**
* Sets a color uniform parameter in the shader.
*
* @param shader The shader to update.
* @param name The name of the uniform parameter.
* @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
);
/**
* Disposes of a shader, freeing any associated resources.
*
* @param shader The shader to dispose.
*/
errorret_t shaderDisposeGL(shadergl_t *shader);
#ifdef DUSK_OPENGL_LEGACY
/**
* During mesh rendering, this is requesting the legacy system to push all
* shaders necessary to render the currently bound shader's matrices.
*
* @return Any error state.
*/
errorret_t shaderLegacyMatrixUpdate();
#endif
@@ -1,19 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "shadergl.h"
typedef shadergl_t shaderplatform_t;
typedef shaderdefinitiongl_t shaderdefinitionplatform_t;
#define shaderInitPlatform shaderInitGL
#define shaderBindPlatform shaderBindGL
#define shaderSetMatrixPlatform shaderSetMatrixGL
#define shaderSetTexturePlatform shaderSetTextureGL
#define shaderSetColorPlatform shaderSetColorGL
#define shaderDisposePlatform shaderDisposeGL
-213
View File
@@ -1,213 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/shader/shaderunlit.h"
#include "assert/assertgl.h"
#ifdef DUSK_OPENGL_LEGACY
shaderdefinition_t SHADER_UNLIT_DEFINITION = {
.setMaterial = shaderUnlitSetMaterial,
};
#else
errorret_t shaderUnlitSetTextureGL(
shadergl_t *shader,
const char_t *name,
texture_t *texture
) {
assertNotNull(shader, "Shader cannot be null");
assertStrLenMin(name, 1, "Uniform name cannot be empty");
assertStringEqual(
name,
SHADER_UNLIT_TEXTURE,
"Only one texture supported in unlit shader."
);
GLint locTexture, locType, locColorCount, locColors;
errorChain(shaderParamGetLocationGL(shader, "u_TextureType", &locType));
// NULL textures
if(texture == NULL) {
glUniform1i(locType, 0);
errorChain(errorGLCheck());
errorOk();
}
// Set texture.
glActiveTexture(GL_TEXTURE0);
errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, texture->id);
errorChain(errorGLCheck());
errorChain(shaderParamGetLocationGL(shader, name, &locTexture));
glUniform1i(locTexture, 0);
errorChain(errorGLCheck());
// Set texture type
if(texture->format == TEXTURE_FORMAT_PALETTE) {
glUniform1i(locType, 2);
errorChain(errorGLCheck());
shaderParamGetLocationGL(shader, "u_ColorCount", &locColorCount);
glUniform1i(locColorCount, texture->palette->count);
errorChain(errorGLCheck());
shaderParamGetLocationGL(shader, "u_Colors", &locColors);
GLuint paletteData[texture->palette->count];
for(size_t i = 0; i < texture->palette->count; i++) {
color_t color = texture->palette->colors[i];
paletteData[i] = (
((uint32_t)color.r << 24) |
((uint32_t)color.g << 16) |
((uint32_t)color.b << 8) |
((uint32_t)color.a << 0)
);
}
glUniform1uiv(locColors, texture->palette->count, paletteData);
errorChain(errorGLCheck());
} else {
glUniform1i(locType, 1);
errorChain(errorGLCheck());
}
errorOk();
}
shaderdefinition_t SHADER_UNLIT_DEFINITION = {
.setMaterial = shaderUnlitSetMaterial,
.setTexture = shaderUnlitSetTextureGL,
.vert =
#ifdef DUSK_OPENGL_ES
"#version 300 es\n"
"precision mediump float;\n"
// Attributes
"layout(location = 0) in vec3 a_Pos;\n"
"layout(location = 1) in vec2 a_TexCoord;\n"
#if MESH_ENABLE_COLOR
"layout(location = 2) in vec4 a_Color;\n"
#endif
// Uniforms
"uniform mat4 u_Proj;\n"
"uniform mat4 u_View;\n"
"uniform mat4 u_Model;\n"
// Vertex shader outputs
"out vec4 v_Color;\n"
"out vec2 v_TexCoord;\n"
"void main() {\n"
" gl_Position = u_Proj * u_View * u_Model * vec4(a_Pos, 1.0);\n"
#if MESH_ENABLE_COLOR
" v_Color = a_Color;\n"
#else
" v_Color = vec4(1.0);\n"
#endif
" v_TexCoord = a_TexCoord;\n"
"}\n",
#else
"#version 330 core\n"
// Attributes
"layout(location = 0) in vec3 a_Pos;\n"
"layout(location = 1) in vec2 a_TexCoord;\n"
#if MESH_ENABLE_COLOR
"layout(location = 2) in vec4 a_Color;\n"
#endif
// Uniforms
"uniform mat4 u_Proj;\n"
"uniform mat4 u_View;\n"
"uniform mat4 u_Model;\n"
// Vertex shader outputs
"out vec4 v_Color;\n"
"out vec2 v_TexCoord;\n"
"void main() {\n"
" gl_Position = u_Proj * u_View * u_Model * vec4(a_Pos, 1.0);\n"
#if MESH_ENABLE_COLOR
" v_Color = a_Color;\n"
#else
" v_Color = vec4(1.0);\n"
#endif
" v_TexCoord = a_TexCoord;\n"
"}\n",
#endif
.frag =
#ifdef DUSK_OPENGL_ES
"#version 300 es\n"
"precision mediump float;\n"
// Uniforms
"uniform sampler2D u_Texture;\n"
"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"
// Fragment shader output
"out vec4 FragColor;\n"
"void main() {\n"
" if(u_TextureType == 0) {\n"// No texture
" FragColor = v_Color * u_Color;\n"
" return;\n"
" }\n"
" if(u_TextureType == 1) {\n"// Regular texture
" FragColor = texture(u_Texture, v_TexCoord) * v_Color * u_Color;\n"
" return;\n"
" }\n"
" if(u_TextureType == 2) {\n"// Paletted texture
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
" uint index = uint(floor(texColor.r * 255.0));\n"
" uint palColor = u_Colors[index];\n"
" float r = float((palColor >> 24) & 0xFFu) / 255.0;\n"
" 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"
" FragColor = vec4(r, g, b, a) * u_Color;\n"
" return;\n"
" }\n"
" FragColor = v_Color * u_Color;\n"// Unknown texture type?
"}\n",
#else
"#version 330 core\n"
// Uniforms
"uniform sampler2D u_Texture;\n"
"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"
// Fragment shader output
"out vec4 FragColor;\n"
"void main() {\n"
" if(u_TextureType == 0) {\n"// No texture
" FragColor = v_Color * u_Color;\n"
" return;\n"
" }\n"
" if(u_TextureType == 1) {\n"// Regular texture
" FragColor = texture(u_Texture, v_TexCoord) * v_Color * u_Color;\n"
" return;\n"
" }\n"
" if(u_TextureType == 2) {\n"// Paletted texture
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
" uint index = uint(floor(texColor.r * 255.0));\n"
" uint palColor = u_Colors[index];\n"
" float r = float((palColor >> 24) & 0xFFu) / 255.0;\n"
" 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"
" FragColor = vec4(r, g, b, a) * u_Color;\n"
" return;\n"
" }\n"
" FragColor = v_Color * u_Color;\n"// Unknown texture type?
"}\n",
#endif
};
#endif
-10
View File
@@ -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
texturegl.c
)
-95
View File
@@ -1,95 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/texture/texture.h"
#include "assert/assert.h"
#include "error/errorgl.h"
#include "util/memory.h"
errorret_t textureInitGL(
texturegl_t *texture,
const int32_t width,
const int32_t height,
const textureformatgl_t format,
const texturedata_t data
) {
glGenTextures(1, &texture->id);
errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, texture->id);
errorChain(errorGLCheck());
switch(format) {
case TEXTURE_FORMAT_RGBA:
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, (void*)data.rgbaColors
);
errorChain(errorGLCheck());
break;
case TEXTURE_FORMAT_PALETTE:
texture->palette = data.paletted.palette;
assertTrue(
texture->palette == &PALETTES[0],
"Only the first palette is supported in legacy opengl."
);
#ifdef DUSK_OPENGL_LEGACY
glColorTableEXT(
GL_TEXTURE_2D, GL_RGBA, texture->palette->count, GL_RGBA,
GL_UNSIGNED_BYTE, (const void*)texture->palette->colors
);
errorChain(errorGLCheck());
glTexImage2D(
GL_TEXTURE_2D,
0, GL_COLOR_INDEX8_EXT,
width, height,
0, GL_COLOR_INDEX8_EXT,
GL_UNSIGNED_BYTE, (void*)data.paletted.indices
);
errorChain(errorGLCheck());
#else
// For modern systems we send to only the R channel and the shader does
// the rest.
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RED, width, height, 0,
GL_RED, GL_UNSIGNED_BYTE, (void*)data.paletted.indices
);
errorChain(errorGLCheck());
#endif
break;
default:
assertUnreachable("Unknown texture format");
break;
}
errorChain(errorGLCheck());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
errorChain(errorGLCheck());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
errorChain(errorGLCheck());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
errorChain(errorGLCheck());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, 0);
errorChain(errorGLCheck());
errorOk();
}
errorret_t textureDisposeGL(texturegl_t *texture) {
assertNotNull(texture, "Texture cannot be NULL");
assertTrue(texture->id != 0, "Texture ID must be valid");
glDeleteTextures(1, &texture->id);
errorChain(errorGLCheck());
errorOk();
}
-54
View File
@@ -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 "dusk.h"
typedef union texturedata_u texturedata_t;
typedef enum {
TEXTURE_FORMAT_RGBA = GL_RGBA,
TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT,
// TEXTURE_FORMAT_DXT5 = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
} textureformatgl_t;
typedef struct {
GLuint id;
textureformatgl_t format;
int32_t width;
int32_t height;
union {
palette_t *palette;
};
} texturegl_t;
/**
* Initializes a texture.
*
* @param texture The texture to initialize.
* @param width The width of the texture.
* @param height The height of the texture.
* @param format The format of the texture (e.g., GL_RGBA, GL_ALPHA).
* @param data The data for the texture, the format changes per format.
* @return An error if the texture failed to initialize, otherwise success.
*/
errorret_t textureInitGL(
texturegl_t *texture,
const int32_t width,
const int32_t height,
const textureformatgl_t format,
const texturedata_t data
);
/**
* Disposes a texture.
*
* @param texture The texture to dispose.
* @return An error if the texture failed to dispose, otherwise success.
*/
errorret_t textureDisposeGL(texturegl_t *texture);
@@ -1,15 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/texture/texturegl.h"
typedef textureformatgl_t textureformatplatform_t;
typedef texturegl_t textureplatform_t;
#define textureInitPlatform textureInitGL
#define textureDisposePlatform textureDisposeGL
+4 -4
View File
@@ -1,17 +1,17 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "errorgl.h"
#include "duskgl.h"
#include "error/errorgl.h"
errorret_t errorGLCheck(void) {
GLenum err = glGetError();
if(err != GL_NO_ERROR) {
errorThrow("GL Error: %d", err);
}
errorOk();
}
}
@@ -5,5 +5,5 @@
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
cutscene.c
rendergl.c
)
+153
View File
@@ -0,0 +1,153 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "render/rendergl.h"
#include "error/errorgl.h"
#include "render/rop.h"
static const char *VERT_SRC =
"#version 330 core\n"
"layout(location=0) in vec2 aPos;\n"
"layout(location=1) in vec4 aColor;\n"
"uniform vec2 uRes;\n"
"out vec4 vColor;\n"
"void main() {\n"
" vec2 clip = (aPos / uRes) * 2.0 - 1.0;\n"
" clip.y = -clip.y;\n"
" gl_Position = vec4(clip, 0.0, 1.0);\n"
" vColor = aColor;\n"
"}\n";
static const char *FRAG_SRC =
"#version 330 core\n"
"in vec4 vColor;\n"
"out vec4 fragColor;\n"
"void main() {\n"
" fragColor = vColor;\n"
"}\n";
typedef struct {
GLuint prog;
GLuint vao;
GLuint vbo;
GLint uRes;
} rendergl_t;
static rendergl_t renderGL;
static GLuint compileShader(GLenum type, const char *src) {
GLuint s = glCreateShader(type);
glShaderSource(s, 1, &src, NULL);
glCompileShader(s);
return s;
}
errorret_t renderGLInit(void) {
GLuint vert = compileShader(GL_VERTEX_SHADER, VERT_SRC);
GLuint frag = compileShader(GL_FRAGMENT_SHADER, FRAG_SRC);
renderGL.prog = glCreateProgram();
glAttachShader(renderGL.prog, vert);
glAttachShader(renderGL.prog, frag);
glLinkProgram(renderGL.prog);
glDeleteShader(vert);
glDeleteShader(frag);
errorChain(errorGLCheck());
renderGL.uRes = glGetUniformLocation(renderGL.prog, "uRes");
glGenVertexArrays(1, &renderGL.vao);
glGenBuffers(1, &renderGL.vbo);
glBindVertexArray(renderGL.vao);
glBindBuffer(GL_ARRAY_BUFFER, renderGL.vbo);
/* 6 verts * (2 pos + 4 color) floats — enough for one sprite */
glBufferData(GL_ARRAY_BUFFER, 6 * 6 * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 6*sizeof(GLfloat), (void*)(2*sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0);
errorChain(errorGLCheck());
errorOk();
}
static void drawSprite(const ropsprite_t *s, float rW, float rH) {
float r = s->tint.r / 255.0f;
float g = s->tint.g / 255.0f;
float b = s->tint.b / 255.0f;
float a = s->tint.a / 255.0f;
float x0 = (float)s->x;
float y0 = (float)s->y;
float x1 = x0 + (float)s->w;
float y1 = y0 + (float)s->h;
GLfloat verts[6][6] = {
{ x0, y1, r,g,b,a },
{ x0, y0, r,g,b,a },
{ x1, y0, r,g,b,a },
{ x0, y1, r,g,b,a },
{ x1, y0, r,g,b,a },
{ x1, y1, r,g,b,a },
};
glBindVertexArray(renderGL.vao);
glBindBuffer(GL_ARRAY_BUFFER, renderGL.vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts);
glDrawArrays(GL_TRIANGLES, 0, 6);
(void)rW; (void)rH;
}
errorret_t renderGLFlush(ropbuffer_t *buf, int winW, int winH) {
glViewport(0, 0, winW, winH);
errorChain(errorGLCheck());
glUseProgram(renderGL.prog);
glUniform2f(renderGL.uRes, (GLfloat)winW, (GLfloat)winH);
errorChain(errorGLCheck());
for(uint32_t i = 0; i < buf->count; i++) {
const ropheader_t *hdr = (const ropheader_t *)(buf->data + i * ROP_SIZE);
switch(hdr->op) {
case ROP_CLEAR: {
const ropclear_t *c = (const ropclear_t *)hdr;
glClearColor(
c->color.r / 255.0f,
c->color.g / 255.0f,
c->color.b / 255.0f,
c->color.a / 255.0f
);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
errorChain(errorGLCheck());
break;
}
case ROP_DRAW_SPRITE: {
const ropsprite_t *s = (const ropsprite_t *)hdr;
drawSprite(s, (float)winW, (float)winH);
errorChain(errorGLCheck());
break;
}
default:
break;
}
}
glUseProgram(0);
glBindVertexArray(0);
errorOk();
}
void renderGLDispose(void) {
if(renderGL.vbo) { glDeleteBuffers(1, &renderGL.vbo); renderGL.vbo = 0; }
if(renderGL.vao) { glDeleteVertexArrays(1, &renderGL.vao); renderGL.vao = 0; }
if(renderGL.prog) { glDeleteProgram(renderGL.prog); renderGL.prog = 0; }
}
+15
View File
@@ -0,0 +1,15 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
#include "error/error.h"
#include "render/ropbuffer.h"
errorret_t renderGLInit(void);
errorret_t renderGLFlush(ropbuffer_t *buf, int winW, int winH);
void renderGLDispose(void);
+5 -6
View File
@@ -1,6 +1,6 @@
/**
* Copyright (c) 2026 Dominic Masters
*
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
@@ -10,8 +10,7 @@
typedef displaysdl2_t displayplatform_t;
#define displayPlatformInit displaySDL2Init
#define displayPlatformUpdate displaySDL2Update
#define displayPlatformSwap displaySDL2Swap
#define displayPlatformSetState displaySDL2SetState
#define displayPlatformDispose displaySDL2Dispose
#define displayPlatformInit displaySDL2Init
#define displayPlatformFlush displaySDL2Flush
#define displayPlatformSwap displaySDL2Swap
#define displayPlatformDispose displaySDL2Dispose

Some files were not shown because too many files have changed in this diff Show More