prog
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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
|
||||
)
|
||||
@@ -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];
|
||||
@@ -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];
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
@@ -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");
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
@@ -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
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "rpg/overworld/chunk.h"
|
||||
|
||||
#define MAP_FILE_PATH_MAX 128
|
||||
|
||||
@@ -10,4 +10,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(overworld)
|
||||
add_subdirectory(overworld)
|
||||
add_subdirectory(test)
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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.
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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
Reference in New Issue
Block a user