This commit is contained in:
2025-04-07 17:57:06 -05:00
parent 7a3d7a5868
commit a779da6c72
98 changed files with 655 additions and 8974 deletions

5
.gitignore vendored
View File

@ -88,4 +88,7 @@ assets/borrowed
*~ *~
/dist /dist
/archive0
/archive1

View File

@ -5,45 +5,10 @@
include(FetchContent) include(FetchContent)
# GLFW # RayLib
FetchContent_Declare( FetchContent_Declare(
glfw raylib
GIT_REPOSITORY https://github.com/glfw/glfw GIT_REPOSITORY https://github.com/raysan5/raylib
GIT_TAG 3.4 GIT_TAG 5.5
) )
FetchContent_MakeAvailable(glfw) FetchContent_MakeAvailable(raylib)
# GLAD (For GLFW)
add_subdirectory(glad)
# CGLM
FetchContent_Declare(
cglm
GIT_REPOSITORY https://github.com/recp/cglm
GIT_TAG v0.9.4
)
FetchContent_MakeAvailable(cglm)
#LibArchive
FetchContent_Declare(
libarchive
GIT_REPOSITORY https://github.com/libarchive/libarchive
GIT_TAG v3.7.6
)
FetchContent_MakeAvailable(libarchive)
#libspng
FetchContent_Declare(
libspng
GIT_REPOSITORY https://github.com/randy408/libspng
GIT_TAG v0.7.4
)
FetchContent_MakeAvailable(libspng)
# SDL2
# FetchContent_Declare(
# SDL2
# GIT_REPOSITORY https://github.com/libsdl-org/SDL
# GIT_TAG release-2.0.14
# )
# FetchContent_MakeAvailable(SDL2)

View File

@ -1,12 +0,0 @@
cmake_minimum_required(VERSION 3.11)
project(glad)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_library(${PROJECT_NAME}
src/glad.c
)
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)

View File

@ -1,311 +0,0 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,30 @@
# Copyright (c) 2025 Dominic Masters # Copyright (c) 2025 Dominic Masters
# #
# This software is released under the MIT License. # This software is released under the MIT License.
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
add_subdirectory(dusktest) # Libs
add_subdirectory(dusk) target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
m
raylib
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
main.c
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(console)
add_subdirectory(error)
add_subdirectory(server)
add_subdirectory(util)

View File

@ -303,15 +303,17 @@ void cmdGet(const consolecmdexec_t *exec) {
} }
void cmdSet(const consolecmdexec_t *exec) { void cmdSet(const consolecmdexec_t *exec) {
assertTrue( assertTrue(exec->argc >= 2, "set command requires 2 arguments.");
exec->argc >= 2,
"set command requires 2 arguments."
);
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) { for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
consolevar_t *var = &CONSOLE.variables[i]; consolevar_t *var = &CONSOLE.variables[i];
if(stringCompare(var->name, exec->argv[0]) != 0) continue; if(stringCompare(var->name, exec->argv[0]) != 0) continue;
consoleVarSetValue(var, exec->argv[1]); consoleVarSetValue(var, exec->argv[1]);
consolePrint("%s %s", var->name, var->value);
for(i = 0; i < var->eventCount; i++) {
assertNotNull(var->events[i], "Event is NULL");
var->events[i](var);
}
return; return;
} }
@ -325,4 +327,58 @@ void cmdEcho(const consolecmdexec_t *exec) {
); );
consolePrint("%s", exec->argv[0]); consolePrint("%s", exec->argv[0]);
}
// May move these later
void consoleUpdate() {
int32_t key = GetKeyPressed();
switch(key) {
case 0:
break;
case KEY_ENTER:
consoleExec(CONSOLE.inputBuffer);
CONSOLE.inputIndex = 0;
CONSOLE.inputBuffer[0] = '\0';
break;
case KEY_BACKSPACE:
if(CONSOLE.inputIndex > 0) {
CONSOLE.inputIndex--;
CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
}
break;
default:
if(key >= 32 && key <= 126 && CONSOLE.inputIndex < CONSOLE_LINE_MAX - 1) {
CONSOLE.inputBuffer[CONSOLE.inputIndex++] = (char_t)GetCharPressed();
CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
}
break;
}
consoleProcess();
}
void consoleDraw() {
size_t i = 0;
char_t *line;
int32_t fontSize = 10;
do {
line = CONSOLE.line[i];
if(line[0] == '\0') {
i++;
continue;
}
DrawText(line, 0, i*fontSize, fontSize, BLACK);
i++;
} while(i < CONSOLE_HISTORY_MAX);
DrawText(
CONSOLE.inputBuffer, 0,
(CONSOLE_HISTORY_MAX + 1) * fontSize,
fontSize,
BLACK
);
} }

View File

@ -38,6 +38,10 @@ typedef struct {
consolecmd_t *cmdGet; consolecmd_t *cmdGet;
consolecmd_t *cmdSet; consolecmd_t *cmdSet;
pthread_mutex_t lock; // Mutex for thread safety pthread_mutex_t lock; // Mutex for thread safety
// May move these later
char_t inputBuffer[CONSOLE_LINE_MAX];
int32_t inputIndex;
} console_t; } console_t;
extern console_t CONSOLE; extern console_t CONSOLE;
@ -93,6 +97,16 @@ void consoleExec(const char_t *line);
*/ */
void consoleProcess(); void consoleProcess();
/**
* Updates the console's input buffer and handles user input.
*/
void consoleUpdate();
/**
* Draws the console's output.
*/
void consoleDraw();
void cmdGet(const consolecmdexec_t *exec); void cmdGet(const consolecmdexec_t *exec);
void cmdSet(const consolecmdexec_t *exec); void cmdSet(const consolecmdexec_t *exec);
void cmdEcho(const consolecmdexec_t *exec); void cmdEcho(const consolecmdexec_t *exec);

View File

@ -16,7 +16,12 @@
#include <stdarg.h> #include <stdarg.h>
#include <ctype.h> #include <ctype.h>
#include <pthread.h> #include <pthread.h>
#include <raylib.h>
#include <errno.h>
#include <unistd.h>
typedef bool bool_t; typedef bool bool_t;
typedef char char_t; typedef char char_t;
#define DUSK_NAME "Dusk"
#define DUSK_VERSION "1.0.0" #define DUSK_VERSION "1.0.0"

View File

@ -1,26 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(console)
add_subdirectory(error)
add_subdirectory(util)

View File

@ -1,19 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct {
} server_t;
extern server_t SERVER;
void serverInit();
void serverStart();
void serverDispose();

View File

@ -1,33 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
archive_static
m
spng_static
cglm
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
asset.c
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(display)
add_subdirectory(overworld)
# Assets
copytool("textures/8x8.png")

View File

@ -1,69 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assertgl.h"
#include "assert/assert.h"
#include "util/memory.h"
void assertNotGLErrorCheck(const char_t *file, const int32_t line) {
assertNotNull(file, "File is an invlaid string");
assertTrue(line > 0, "assertNotGLErrorCheck: line is invalid");
char_t *buffer;
GLenum errorCode;
int32_t errorCount = 0;
while((errorCode = glGetError()) != GL_NO_ERROR) {
if(errorCount == 0) {
buffer = memoryAllocate(sizeof(char_t) * 4096);
sprintf(buffer, "assertNotGLErrorCheck: %s:%d\n", file, line);
}
errorCount++;
switch (errorCode) {
case GL_INVALID_ENUM:
sprintf(buffer, "%s\nINVALID_ENUM", buffer);
break;
case GL_INVALID_VALUE:
sprintf(buffer, "%s\nINVALID_ENUM", buffer);
break;
case GL_INVALID_OPERATION:
sprintf(buffer, "%s\nINVALID_OPERATION", buffer);
break;
// case GL_INVALID_FRAMEBUFFER_OPERATION:
// sprintf(buffer, "%s\nINVALID_FRAMEBUFFER_OPERATION", buffer);
// break;
case GL_OUT_OF_MEMORY:
sprintf(buffer, "%s\nOUT_OF_MEMORY", buffer);
break;
case GL_STACK_UNDERFLOW:
sprintf(buffer, "%s\nSTACK_UNDERFLOW", buffer);
break;
case GL_STACK_OVERFLOW:
sprintf(buffer, "%s\nSTACK_OVERFLOW", buffer);
break;
default:
sprintf(buffer, "%s\nUNKNOWN_ERROR", buffer);
break;
}
sprintf(buffer, "%s (%i)\n", buffer, errorCode);
}
if(errorCount > 0) {
assertUnreachable(buffer);
free(buffer);
}
}

View File

@ -1,22 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
/**
* Asserts that there are no OpenGL errors.
*
* @param file The file the assertion is being made in.
* @param line The line the assertion is being made in.
*/
void assertNotGLErrorCheck(const char_t *file, const int32_t line);
/**
* Asserts that there are no OpenGL errors.
*/
#define assertNoGLError() assertNotGLErrorCheck(__FILE__, __LINE__)

View File

@ -1,223 +0,0 @@
/**
* Copyright (c) 2024 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset.h"
#include "assert/assert.h"
#include "util/math.h"
#include "util/memory.h"
asset_t ASSET;
void assetInit() {
const char_t *assetFilename = "dusk.tar";
char_t assetPath[FILENAME_MAX];
const char_t* scanLocations[] = {
EXECUTABLE_DIRECTORY
};
memoryZero(&ASSET, sizeof(asset_t));
// Try and find the asset file.
for(int32_t i = 0; i < sizeof(scanLocations) / sizeof(char_t*); i++) {
sprintf(assetPath, "%s/%s", scanLocations[i], assetFilename);
FILE *file = fopen(assetPath, "rb");
if(file == NULL) continue;
// File found.
ASSET.file = file;
}
// Ensure we found it.
assertNotNull(ASSET.file, "Failed to find asset file!");
}
void assetOpen(const char_t *path) {
assertNotNull(path, "Path is not valid!");
assertStrLen(path, FILENAME_MAX, "Path is too long!");
assertStrLenMin(path, 1, "Path is empty!");
// Make sure things are clean
assertNull(ASSET.archive, "Archive is not NULL!");
assertNull(ASSET.entry, "Entry is not NULL!");
assertNotNull(ASSET.file, "File is NULL!");
// Store path
strcpy(ASSET.path, path);
// Prepare data
ASSET.archive = archive_read_new();
assertNotNull(ASSET.archive, "Failed to init archive reader");
// Set up the reader
// archive_read_support_filter_bzip2(ASSET_ARCHIVE);
archive_read_support_format_tar(ASSET.archive);
// Set the archive reader callbacks
archive_read_set_open_callback(ASSET.archive, &assetArchiveOpen);
archive_read_set_read_callback(ASSET.archive, &assetArchiveRead);
archive_read_set_seek_callback(ASSET.archive, &assetArchiveSeek);
archive_read_set_close_callback(ASSET.archive, &assetArchiveClose);
archive_read_set_callback_data(ASSET.archive, ASSET.buffer);// TODO: Not needed?
// Open the archive
int32_t ret = archive_read_open1(ASSET.archive);
assertTrue(ret == ARCHIVE_OK, "Failed to open archive!");
// Iterate over each file.
while(archive_read_next_header(ASSET.archive, &ASSET.entry) == ARCHIVE_OK) {
// What file is at this position?
const char_t *headerFile = (char_t*)archive_entry_pathname(ASSET.entry);
// Compare if this is the file we are looking for, if it is just exit the
// function
if(strcmp(headerFile, ASSET.path) == 0) return;
// It is not, skip it.
int32_t ret = archive_read_data_skip(ASSET.archive);
assertTrue(ret == ARCHIVE_OK, "Failed to skip data!");
}
// If we get here we did not find the find in the archive.
assertUnreachable("Failed to find file!");
}
size_t assetGetSize() {
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
assertTrue(archive_entry_size_is_set(ASSET.entry), "Entry size is not set!");
return archive_entry_size(ASSET.entry);
}
size_t assetRead(
uint8_t *buffer,
const size_t bufferSize
) {
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
assertNotNull(buffer, "Buffer is NULL!");
assertTrue(bufferSize > 0, "Buffer size must be greater than 0!");
ssize_t read = archive_read_data(ASSET.archive, buffer, bufferSize);
if(read == ARCHIVE_FATAL) {
assertUnreachable(archive_error_string(ASSET.archive));
return -1;
}
assertTrue(read != ARCHIVE_RETRY, "Failed to read data (RETRY)!");
assertTrue(read != ARCHIVE_WARN, "Failed to read data (WARN)!");
return read;
}
size_t assetReadUntil(
uint8_t *buffer,
const char_t c,
const size_t maxLength
) {
if(buffer == NULL) {
assertTrue(
maxLength == -1, "If no buffer is provided, maxLength must be -1."
);
uint8_t tBuffer[1];
size_t read = 0;
while(assetRead(tBuffer, 1) == 1 && (char_t)tBuffer[0] != c) read++;
return read;
}
size_t read = 0;
while(read < maxLength) {
// TODO: Read more than 1 char at a time.
read += assetRead(buffer + read, 1);
if((char_t)buffer[read-1] == c) return read - 1;
}
return -1;
}
void assetSkip(const size_t length) {
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
// Asset archive does not support skipping, so we have to read and discard.
uint8_t buffer[ASSET_BUFFER_SIZE];
size_t remaining = length;
do {
size_t toRead = mathMin(remaining, ASSET_BUFFER_SIZE);
size_t read = assetRead(buffer, toRead);
assertTrue(read == toRead, "Failed to skip data! (overskip?)");
remaining -= read;
} while(remaining > 0);
}
void assetClose() {
if(ASSET.archive == NULL) return;
assertNotNull(ASSET.archive, "Archive is NULL!");
assertNotNull(ASSET.entry, "Entry is NULL!");
int32_t ret = archive_read_free(ASSET.archive);
assertTrue(ret == ARCHIVE_OK, "Failed to close archive!");
assertNull(ASSET.archive, "Archive is not NULL? Cleanup incorrect.");
}
void assetDispose() {
assertNull(ASSET.archive, "Asset disposing but asset is currently open?");
assertNull(ASSET.entry, "Asset disposing but entry is currently open?");
assertNotNull(ASSET.file, "Asset disposing but file is NULL?");
fclose(ASSET.file);
memoryZero(&ASSET, sizeof(asset_t));
}
// Libarchive callbacks
ssize_t assetArchiveRead(
struct archive *archive,
void *data,
const void **buffer
) {
assertNotNull(archive, "Archive is NULL!");
assertNotNull(data, "Data is NULL!");
assertNotNull(buffer, "Buffer is NULL!");
*buffer = data;
size_t read = fread(data, 1, ASSET_BUFFER_SIZE, ASSET.file);
if(ferror(ASSET.file)) return ARCHIVE_FATAL;
return read;
}
int64_t assetArchiveSeek(
struct archive *archive,
void *data,
int64_t offset,
int32_t whence
) {
assertNotNull(archive, "Archive is NULL!");
assertNotNull(data, "Data is NULL!");
assertTrue(offset > 0, "Offset must be greater than 0!");
assertNotNull(ASSET.file, "File is NULL!");
int32_t ret = fseek(ASSET.file, offset, whence);
assertTrue(ret == 0, "Failed to seek!");
return ftell(ASSET.file);
}
int32_t assetArchiveOpen(struct archive *a, void *data) {
int32_t ret = fseek(ASSET.file, 0, SEEK_SET);
assertTrue(ret == 0, "Failed to seek to start of file!");
return ARCHIVE_OK;
}
int32_t assetArchiveClose(struct archive *a, void *data) {
assertNotNull(ASSET.file, "File is NULL!");
ASSET.archive = NULL;
ASSET.entry = NULL;
return ARCHIVE_OK;
}

View File

@ -1,137 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
#include <archive.h>
#include <archive_entry.h>
#define ASSET_BUFFER_SIZE 32768
typedef struct {
FILE *file;
struct archive *archive;
struct archive_entry *entry;
uint8_t buffer[ASSET_BUFFER_SIZE];
// ?
char_t path[FILENAME_MAX];
} asset_t;
extern asset_t ASSET;
/**
* Initializes the asset manager.
*/
void assetInit();
/**
* Opens an asset by its filename (within the asset archive). Asset paths should
* always use the unix forward slash '/' as a path separator.
*
* @param path The path to the asset within the archive.
*/
void assetOpen(const char *path);
/**
* Returns the size of the asset.
*
* @return The size of the asset.
*/
size_t assetGetSize();
/**
* Reads the asset into the buffer.
*
* @param buffer The buffer to read the asset into.
* @param bufferSize The size of the buffer.
* @return The amount of data read.
*/
size_t assetRead(uint8_t *buffer, const size_t bufferSize);
/**
* Reads ahead in the buffer until either the end of the buffer, or the
* specified character is found. Return value will be -1 if the character was
* not found.
*
* Buffer can be NULL if you just want to skip ahead.
*
* Returned value will be either the amount of data read into the buffer, which
* excludes the extra 1 character that was read from the asset. If the character
* was not found, -1 will be returned.
*
* @param buffer Buffer to read into.
* @param c Character to read until.
* @param maxLength Maximum length to read.
* @return -1 if the character was not found, otherwise the amount of data read.
*/
size_t assetReadUntil(uint8_t *buffer, const char c, const size_t maxLength);
/**
* Skips ahead in the buffer by the specified length.
*
* @param length The length to skip ahead by.
*/
void assetSkip(const size_t length);
/**
* Closes the asset.
*/
void assetClose();
/**
* Destroys and cleans up the asset manager.
*/
void assetDispose();
/**
* Internal read method provided to libarchive api.
*
* @param archive The archive to read from.
* @param data The data to read into.
* @param buffer The buffer to read from.
* @return The amount of data read.
*/
ssize_t assetArchiveRead(
struct archive *archive,
void *data,
const void **buffer
);
/**
* Internal seek method provided to libarchive api.
*
* @param archive The archive to seek in.
* @param data The data to seek in.
* @param offset Offset bytes to seek.
* @param whence Relative to whence to seek.
* @return The new position.
*/
int64_t assetArchiveSeek(
struct archive *archive,
void *data,
int64_t offset,
int32_t whence
);
/**
* Internal open method provided to libarchive api.
*
* @param archive The archive to open.
* @param data The data to open.
* @return The result of the open.
*/
int32_t assetArchiveOpen(struct archive *a, void *data);
/**
* Internal close method provided to libarchive api.
*
* @param archive The archive to close.
* @param data The data to close.
* @return The result of the close.
*/
int32_t assetArchiveClose(struct archive *a, void *data);

View File

@ -1,16 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
render.c
quad.c
texture.c
tilesetgl.c
)
# Subdirs
add_subdirectory(shader)

View File

@ -1,130 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "framebuffer.h"
#include "assert/assert.h"
#include "assert/assertgl.h"
#include "display/window.h"
framebuffer_t FRAMEBUFFER;
void frameBufferInit(const int32_t width, const int32_t height) {
assertTrue(width > 0, "Width must be greater than 0");
assertTrue(height > 0, "Height must be greater than 0");
FRAMEBUFFER.id = -1;
FRAMEBUFFER.width = -1;
FRAMEBUFFER.height = -1;
FRAMEBUFFER.texture = -1;
frameBufferSetSize(width, height);
}
void frameBufferSetSize(const int32_t width, const int32_t height) {
assertTrue(width > 0, "Width must be greater than 0");
assertTrue(height > 0, "Height must be greater than 0");
if(FRAMEBUFFER.width == width && FRAMEBUFFER.height == height) return;
FRAMEBUFFER.width = width;
FRAMEBUFFER.height = height;
frameBufferUnbind();
// Delete old texture and depth buffer
if(FRAMEBUFFER.texture != -1) {
glDeleteTextures(1, &FRAMEBUFFER.texture);
assertNoGLError();
FRAMEBUFFER.texture = -1;
}
if(FRAMEBUFFER.id != -1) {
glDeleteFramebuffers(1, &FRAMEBUFFER.id);
assertNoGLError();
FRAMEBUFFER.id = -1;
}
// Generate framebuffer
glGenFramebuffers(1, &FRAMEBUFFER.id);
assertNoGLError();
glBindFramebuffer(GL_FRAMEBUFFER, FRAMEBUFFER.id);
assertNoGLError();
// Create new texture
glGenTextures(1, &FRAMEBUFFER.texture);
assertNoGLError();
glBindTexture(GL_TEXTURE_2D, FRAMEBUFFER.texture);
assertNoGLError();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
assertNoGLError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
assertNoGLError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
assertNoGLError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
assertNoGLError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
assertNoGLError();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FRAMEBUFFER.texture, 0);
assertNoGLError();
// Check framebuffer completeness
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
assertNoGLError();
assertTrue(status == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is not complete");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
assertNoGLError();
}
void frameBufferBind() {
if(FRAMEBUFFER.id == -1) return;
glBindFramebuffer(GL_FRAMEBUFFER, FRAMEBUFFER.id);
assertNoGLError();
glViewport(0, 0, FRAMEBUFFER.width, FRAMEBUFFER.height);
assertNoGLError();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
assertNoGLError();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
assertNoGLError();
}
void frameBufferTextureBind(const GLuint slot) {
if(FRAMEBUFFER.texture == -1) return;
glActiveTexture(GL_TEXTURE0 + slot);
assertNoGLError();
glBindTexture(GL_TEXTURE_2D, FRAMEBUFFER.texture);
assertNoGLError();
}
void frameBufferUnbind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
assertNoGLError();
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
assertNoGLError();
glClearColor(0.05f, 0.05f, 0.05f, 1.0f);
assertNoGLError();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
assertNoGLError();
}
void frameBufferDispose() {
glDeleteFramebuffers(1, &FRAMEBUFFER.id);
glDeleteTextures(1, &FRAMEBUFFER.texture);
FRAMEBUFFER.id = -1;
FRAMEBUFFER.texture = -1;
}

View File

@ -1,56 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
typedef struct {
GLuint id;
GLuint texture;
int width;
int height;
} framebuffer_t;
extern framebuffer_t FRAMEBUFFER;
/**
* Initializes the framebuffer.
*
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
*/
void frameBufferInit(const int32_t width, const int32_t height);
/**
* Sets the size of the framebuffer.
*
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
*/
void frameBufferSetSize(const int32_t width, const int32_t height);
/**
* Binds the framebuffer.
*/
void frameBufferBind();
/**
* Unbinds the framebuffer.
*/
void frameBufferUnbind();
/**
* Binds the framebuffer texture to a slot.
*
* @param slot The slot to bind the texture to.
*/
void frameBufferTextureBind(const GLuint slot);
/**
* Unbinds the framebuffer.
*/
void frameBufferDispose();

View File

@ -1,108 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assertgl.h"
#include "quad.h"
duskquad_t QUAD;
void quadInit() {
// Create the single quad.
const float quadPositions[] = {
0.0f, 0.0f, 0.0f, // Bottom-left corner
1.0f, 0.0f, 0.0f, // Bottom-right corner
1.0f, 1.0f, 0.0f, // Top-right corner
0.0f, 1.0f, 0.0f // Top-left corner
};
const float quadUVs[] = {
0.0f, 0.0f,
1.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
const uint32_t quadIndices[] = {
0, 1, 2,
2, 3, 0,
};
glGenVertexArrays(1, &QUAD.quadVAO);
assertNoGLError();
glBindVertexArray(QUAD.quadVAO);
assertNoGLError();
// Positions
glGenBuffers(1, &QUAD.quadVBO);
assertNoGLError();
glBindBuffer(GL_ARRAY_BUFFER, QUAD.quadVBO);
assertNoGLError();
glBufferData(
GL_ARRAY_BUFFER, sizeof(quadPositions), quadPositions, GL_STATIC_DRAW
);
assertNoGLError();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
assertNoGLError();
glEnableVertexAttribArray(0);
assertNoGLError();
// UVs
glGenBuffers(1, &QUAD.quadUVVBO);
assertNoGLError();
glBindBuffer(GL_ARRAY_BUFFER, QUAD.quadUVVBO);
assertNoGLError();
glBufferData(GL_ARRAY_BUFFER, sizeof(quadUVs), quadUVs, GL_STATIC_DRAW);
assertNoGLError();
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
assertNoGLError();
glEnableVertexAttribArray(1);
assertNoGLError();
// Indices
glGenBuffers(1, &QUAD.quadEBO);
assertNoGLError();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, QUAD.quadEBO);
assertNoGLError();
glBufferData(
GL_ELEMENT_ARRAY_BUFFER, sizeof(quadIndices), quadIndices, GL_STATIC_DRAW
);
assertNoGLError();
}
void quadRender(const int32_t count) {
glBindVertexArray(QUAD.quadVAO);
assertNoGLError();
glDrawElementsInstanced(
GL_TRIANGLES, 6,
GL_UNSIGNED_INT, 0,
count
);
assertNoGLError();
}
void quadDispose() {
glDeleteBuffers(1, &QUAD.quadVBO);
assertNoGLError();
glDeleteBuffers(1, &QUAD.quadEBO);
assertNoGLError();
glDeleteVertexArrays(1, &QUAD.quadVAO);
assertNoGLError();
}

View File

@ -1,35 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
typedef struct {
GLuint quadVBO;
GLuint quadUVVBO;
GLuint quadVAO;
GLuint quadEBO;
} duskquad_t;
extern duskquad_t QUAD;
/**
* Initializes the quad.
*/
void quadInit();
/**
* Renders quads.
*
* @param count The number of quads to render.
*/
void quadRender(const int32_t count);
/**
* Disposes of the quad.
*/
void quadDispose();

View File

@ -1,52 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assertgl.h"
#include "util/memory.h"
#include "render.h"
#include "display/scene.h"
#include "display/shader/shadermanager.h"
#include "display/quad.h"
#include "display/window.h"
#include "display/tilesetgl.h"
render_t RENDER;
void renderInit() {
memoryZero(&RENDER, sizeof(render_t));
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
assertNoGLError();
glEnable(GL_BLEND);
assertNoGLError();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
assertNoGLError();
shaderManagerInit();
quadInit();
}
void renderUpdate() {
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
assertNoGLError();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
assertNoGLError();
// Update rendering data
shaderManagerUpdate();
tilesetGLBind();
// Hand off to the scene to do its rendering.
sceneRender();
}
void renderDispose() {
quadDispose();
shaderManagerDispose();
}

View File

@ -1,35 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
typedef struct {
int32_t nothing;
} render_t;
extern render_t RENDER;
/**
* Initializes the render system.
*/
void renderInit();
/**
* Updates the render system.
*/
void renderUpdate();
/**
* Renders the overworld scene.
*/
void renderOverworld();
/**
* Disposes of the render system.
*/
void renderDispose();

View File

@ -1,18 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
shadermanager.c
shader.c
shaderbuffer.c
)
# Subdirs
add_subdirectory(data)
add_subdirectory(entityshader)
add_subdirectory(mapshader)
add_subdirectory(fragments)

View File

@ -1,13 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
transforms.c
entities.c
mapshaderdata.c
tilesetshaderdata.c
)

View File

@ -1,42 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entities.h"
#include "overworld/overworld.h"
#include "assert/assert.h"
#include "util/memory.h"
shaderbuffer_t ENTITIES_BUFFER;
entitiesdata_t ENTITIES_DATA;
void entitiesInit() {
memoryZero(&ENTITIES_DATA, sizeof(entitiesdata_t));
shaderBufferInit(&ENTITIES_BUFFER, sizeof(entitiesdata_t));
}
void entitiesUpdate() {
for(uint8_t i = 0; i < OVERWORLD.entityCount; i++) {
// Pack the entity data into the buffer
entity_t *src = &OVERWORLD.entities[i];
entitiesdataent_t *dst = &ENTITIES_DATA.entities[i];
// Copy position data.
memoryCopyRangeSafe(
&dst->position,
&src->x,
&src->frame + sizeof(uint8_t),
sizeof(uint8_t) * 5
);
}
shaderBufferBind(&ENTITIES_BUFFER);
shaderBufferSetData(&ENTITIES_BUFFER, &ENTITIES_DATA);
}
void entitiesDispose() {
shaderBufferDispose(&ENTITIES_BUFFER);
}

View File

@ -1,48 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../../../../dusk/overworld/overworlddefs.h"
#include "../fragments/packed.glsl"
#include "../fragments/quad.glsl"
#include "../data/tilesets.glsl"
struct Entity {
uvec4 position;
};
layout(std140) uniform b_Entities {
Entity entities[OVERWORLD_ENTITY_COUNT_MAX];
};
vec2 entityGetSize() {
return vec2(float(OVERWORLD_ENTITY_WIDTH), float(OVERWORLD_ENTITY_HEIGHT));
}
vec2 entityGetVertice(uint instanceIndex, uint indiceIndex) {
// Get base quad vertice
vec2 vert = quadGetVertice(indiceIndex);
uint x = packedGetU8(0u, entities[instanceIndex].position);
uint y = packedGetU8(1u, entities[instanceIndex].position);
int subX = packedGetI8(2u, entities[instanceIndex].position);
int subY = packedGetI8(3u, entities[instanceIndex].position);
vert.x += float(x);
vert.y += float(y);
vert *= entityGetSize();
vert.x += float(subX);
vert.y += float(subY);
return vert;
}
vec2 entityGetUV(uint instanceIndex, uint indiceIndex) {
uint frame = packedGetU8(4u, entities[instanceIndex].position);
vec4 tilesetUVs = tilesetGetUVsByIndex(uint(TILESET_SLOT_ENTITIES), frame);
vec2 uv = quadGetTextureCoordinate(indiceIndex, tilesetUVs);
return uv;
}

View File

@ -1,38 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/shader/shaderbuffer.h"
#include "overworld/overworld.h"
#define ENTITIES_BLOCK_NAME "b_Entities"
typedef struct {
uvec4_t position;
} entitiesdataent_t;
typedef struct {
entitiesdataent_t entities[OVERWORLD_ENTITY_COUNT_MAX];
} entitiesdata_t;
extern shaderbuffer_t ENTITIES_BUFFER;
extern entitiesdata_t ENTITIES_DATA;
/**
* Initializes the entities buffer and data.
*/
void entitiesInit();
/**
* Updates the entities buffer with the current data.
*/
void entitiesUpdate();
/**
* Destroys the entities buffer.
*/
void entitiesDispose();

View File

@ -1,48 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../../../../dusk/overworld/overworlddefs.h"
#include "../fragments/packed.glsl"
#include "../fragments/quad.glsl"
#define MAP_TILE_PACKED_SIZE 16
layout(std140) uniform b_Map {
uvec4 mapTileIds[OVERWORLD_TILE_COUNT_MAX / MAP_TILE_PACKED_SIZE];
uvec4 mapSize;
};
vec2 mapTileGetSize() {
return vec2(float(OVERWORLD_TILE_WIDTH), float(OVERWORLD_TILE_HEIGHT));
}
vec2 mapGetVertice(uint instanceIndex, uint indiceIndex) {
vec2 quad = quadGetVertice(indiceIndex);
uint mapWidth = packedGetU8(0u, mapSize);
uint mapHeight = packedGetU8(1u, mapSize);
uint mapLayerCount = packedGetU8(2u, mapSize);
// Get x and y within layer
uint x = instanceIndex % mapWidth;
uint y = (instanceIndex / mapWidth) % mapHeight;
// Get quad position, consider layer count
quad += vec2(x, y);
quad *= mapTileGetSize();
return quad;
}
uint mapGetLayer(uint instanceIndex) {
uint mapWidth = packedGetU8(0u, mapSize);
uint mapHeight = packedGetU8(1u, mapSize);
return instanceIndex / (mapWidth * mapHeight);
}
uint mapGetTileId(uint instanceIndex) {
uvec4 v4 = mapTileIds[packedArrayGetU8IndexFromUVEC4Array(instanceIndex)];
return packedArrayGetU8FromUVEC4ArrayValue(instanceIndex, v4);
}

View File

@ -1,50 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mapshaderdata.h"
#include "assert/assert.h"
#include "util/memory.h"
shaderbuffer_t MAP_SHADER_DATA_BUFFER;
mapshaderdata_t MAP_SHADER_DATA_DATA;
void mapShaderDataInit() {
memoryZero(&MAP_SHADER_DATA_DATA, sizeof(mapshaderdata_t));
shaderBufferInit(&MAP_SHADER_DATA_BUFFER, sizeof(mapshaderdata_t));
assertTrue(
sizeof(MAP_SHADER_DATA_DATA.tileIds) == sizeof(OVERWORLD.map.tileIds),
"Map shader tile data and Overworld tile data are not the same size."
);
}
void mapShaderDataUpdate() {
// Copy tile ids.
memoryCopyRangeSafe(
MAP_SHADER_DATA_DATA.tileIds,
OVERWORLD.map.tileIds,
&OVERWORLD.map.tileIds[
OVERWORLD.map.width * OVERWORLD.map.height * OVERWORLD.map.layerCount
],
sizeof(MAP_SHADER_DATA_DATA.tileIds)
);
// Copy map size.
memoryCopyRangeSafe(
&MAP_SHADER_DATA_DATA.mapSize,
&OVERWORLD.map.width,
&OVERWORLD.map.layerCount + sizeof(uint8_t),
sizeof(uint8_t) * 3
);
shaderBufferBind(&MAP_SHADER_DATA_BUFFER);
shaderBufferSetData(&MAP_SHADER_DATA_BUFFER, &MAP_SHADER_DATA_DATA);
}
void mapShaderDataDispose() {
shaderBufferDispose(&MAP_SHADER_DATA_BUFFER);
}

View File

@ -1,36 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma ocne
#include "display/shader/shaderbuffer.h"
#include "overworld/overworld.h"
#define MAP_BLOCK_NAME "b_Map"
#define MAP_TILE_PACK_SIZE sizeof(uvec4_t) / sizeof(tileid_t)
typedef struct {
uvec4_t tileIds[OVERWORLD_TILE_COUNT_MAX / MAP_TILE_PACK_SIZE];
uvec4_t mapSize;
} mapshaderdata_t;
extern shaderbuffer_t MAP_SHADER_DATA_BUFFER;
extern mapshaderdata_t MAP_SHADER_DATA_DATA;
/**
* Initializes the map buffer and data.
*/
void mapShaderDataInit();
/**
* Updates the map buffer with the current data.
*/
void mapShaderDataUpdate();
/**
* Destroys the map buffer.
*/
void mapShaderDataDispose();

View File

@ -1,64 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../../../../dusk/display/tilesetdefs.h"
#include "../fragments/packed.glsl"
struct Tileset {
uvec4 tileset;
};
layout(std140) uniform b_Tilesets {
Tileset tilesets[TILESET_SLOT_COUNT];
};
uniform sampler2D u_TilesetTextures[TILESET_SLOT_COUNT];
uint tilesetGetColumns(uint tilesetIndex) {
return packedGetU32(0u, tilesets[tilesetIndex].tileset);
}
uint tilesetGetRows(uint tilesetIndex) {
return packedGetU32(1u, tilesets[tilesetIndex].tileset);
}
uint tilesetGetWidth(uint tilesetIndex) {
return packedGetU32(2u, tilesets[tilesetIndex].tileset);
}
uint tilesetGetHeight(uint tilesetIndex) {
return packedGetU32(3u, tilesets[tilesetIndex].tileset);
}
vec4 tilesetGetUVs(uint tilesetIndex, uint col, uint row) {
Tileset tileset = tilesets[tilesetIndex];
float segWidth = 1.0 / float(tilesetGetColumns(tilesetIndex));
float segHeight = 1.0 / float(tilesetGetRows(tilesetIndex));
float x = float(col) * segWidth;
float y = float(row) * segHeight;
return vec4(x, y, x + segWidth, y + segHeight);
}
vec4 tilesetGetUVsByIndex(uint tilesetIndex, uint index) {
Tileset tileset = tilesets[tilesetIndex];
uint col = index % tilesetGetColumns(tilesetIndex);
uint row = index / tilesetGetColumns(tilesetIndex);
return tilesetGetUVs(tilesetIndex, col, row);
}
vec4 tilesetGetColor(uint tilesetIndex, vec2 coord) {
switch(tilesetIndex) {
case 0u:
return texture(u_TilesetTextures[0], coord);
case 1u:
return texture(u_TilesetTextures[1], coord);
case 2u:
return texture(u_TilesetTextures[2], coord);
case 3u:
return texture(u_TilesetTextures[3], coord);
default:
return vec4(1, 1, 1, 1);
}
}

View File

@ -1,57 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "tilesetshaderdata.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "display/shader/shader.h"
shaderbuffer_t TILESET_SHADER_DATA_BUFFER;
tilesetshaderdata_t TILESET_SHADER_DATA_DATA;
GLuint TILESET_SHADER_DATA_TEXTURES[TILESET_SLOT_COUNT];
void tilesetShaderDataInit() {
memoryZero(&TILESET_SHADER_DATA_DATA, sizeof(tilesetshaderdata_t));
shaderBufferInit(&TILESET_SHADER_DATA_BUFFER, sizeof(tilesetshaderdata_t));
assertTrue(
sizeof(tilesetshaderdata_t) == sizeof(uvec4_t) * TILESET_SLOT_COUNT,
"Tileset Shader Data size mismatch"
);
uint8_t i = 0;
do {
TILESET_SHADER_DATA_TEXTURES[i] = i;
} while(++i < TILESET_SLOT_COUNT);
}
void tilesetShaderDataUpdate() {
uint8_t i = 0;
do {
tilesetid_t id = TILESET_SLOTS[i];
if(id == TILESET_NULL) continue;
tileset_t *tileset = &TILESETS[id];
texture_t *texture = &TILESET_GL_TEXTURES[i];
tilesetshaderdatatileset_t *dest = &TILESET_SHADER_DATA_DATA.tilesets[i];
dest->columns = tileset->columns;
dest->rows = tileset->rows;
dest->width = texture->width;
dest->height = texture->height;
} while(++i < TILESET_SLOT_COUNT);
shaderBufferBind(&TILESET_SHADER_DATA_BUFFER);
shaderBufferSetData(&TILESET_SHADER_DATA_BUFFER, &TILESET_SHADER_DATA_DATA);
}
void tilesetShaderTexturesBind(const GLuint uniform) {
shaderSetTextures(uniform, TILESET_SHADER_DATA_TEXTURES, TILESET_SLOT_COUNT);
}
void tilesetShaderDataDispose() {
shaderBufferDispose(&TILESET_SHADER_DATA_BUFFER);
}

View File

@ -1,50 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/shader/shaderbuffer.h"
#include "display/tilesetgl.h"
#define TILESET_SHADER_DATA_BLOCK_NAME "b_Tilesets"
#define TILESET_UNIFORM_TEXTURES_NAME "u_TilesetTextures"
typedef struct {
uint32_t columns;
uint32_t rows;
uint32_t width;
uint32_t height;
} tilesetshaderdatatileset_t;
typedef struct {
tilesetshaderdatatileset_t tilesets[TILESET_SLOT_COUNT];
} tilesetshaderdata_t;
extern shaderbuffer_t TILESET_SHADER_DATA_BUFFER;
extern tilesetshaderdata_t TILESET_SHADER_DATA_DATA;
extern GLuint TILESET_SHADER_DATA_TEXTURES[];
/**
* Initializes the tileset buffer and data.
*/
void tilesetShaderDataInit();
/**
* Updates the tileset buffer with the current data.
*/
void tilesetShaderDataUpdate();
/**
* Binds the tileset textures to the given uniform.
*
* @param uniform The uniform to bind the textures to.
*/
void tilesetShaderTexturesBind(const GLuint uniform);
/**
* Destroys the tileset buffer.
*/
void tilesetShaderDataDispose();

View File

@ -1,50 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "transforms.h"
#include "display/window.h"
shaderbuffer_t TRANSFORMS_BUFFER;
transformsdata_t TRANSFORMS_DATA;
void transformsInit() {
memset(&TRANSFORMS_DATA, 0, sizeof(transformsdata_t));
shaderBufferInit(&TRANSFORMS_BUFFER, sizeof(transformsdata_t));
glm_mat4_identity(TRANSFORMS_DATA.projection);
glm_mat4_identity(TRANSFORMS_DATA.view);
TRANSFORMS_DATA.resolution[0] = WINDOW_WIDTH;
TRANSFORMS_DATA.resolution[1] = WINDOW_HEIGHT;
}
void transformsUpdate() {
TRANSFORMS_DATA.resolution[0] = WINDOW_WIDTH;
TRANSFORMS_DATA.resolution[1] = WINDOW_HEIGHT;
glm_perspective(
glm_rad(45.0f),
TRANSFORMS_DATA.resolution[0] / TRANSFORMS_DATA.resolution[1],
0.5f,
1000.0f,
TRANSFORMS_DATA.projection
);
glm_lookat(
(vec3){ 0, 0, 300 },
(vec3){ 0, 32, 0 },
(vec3){ 0, 1, 0 },
TRANSFORMS_DATA.view
);
shaderBufferBind(&TRANSFORMS_BUFFER);
shaderBufferSetData(&TRANSFORMS_BUFFER, &TRANSFORMS_DATA);
}
void transformsDispose() {
shaderBufferDispose(&TRANSFORMS_BUFFER);
}

View File

@ -1,32 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../../../../dusk/display/displaydefs.h"
struct Transform {
mat4 projection;
mat4 view;
vec2 resolution;
};
layout(std140) uniform b_Transforms {
Transform transforms;
};
vec2 transformDisplayGetSize() {
return vec2(SCREEN_WIDTH, SCREEN_HEIGHT);
}
float transformDisplayGetAspectRatio() {
return (float(SCREEN_WIDTH) / float(SCREEN_HEIGHT));
}
vec2 transformResolutionGetSize() {
return transforms.resolution;
}
float transformResolutionGetAspectRatio() {
return (transforms.resolution.x / transforms.resolution.y);
}

View File

@ -1,35 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/shader/shaderbuffer.h"
#define TRANSFORMS_BLOCK_NAME "b_Transforms"
typedef struct {
mat4 projection;
mat4 view;
vec2 resolution;
} transformsdata_t;
extern shaderbuffer_t TRANSFORMS_BUFFER;
extern transformsdata_t TRANSFORMS_DATA;
/**
* Initializes the transforms buffer and data.
*/
void transformsInit();
/**
* Updates the transforms buffer with the current data.
*/
void transformsUpdate();
/**
* Destroys the transforms buffer.
*/
void transformsDispose();

View File

@ -1,14 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Shaders
glsltool(entity_vert.glsl)
glsltool(entity_frag.glsl)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
entityshader.c
)

View File

@ -1,21 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../fragments/header.glsl"
#include "../data/entities.glsl"
#include "../data/tilesets.glsl"
// Inputs from vertex shader
in vec2 v_TextureCoord;
// Frag pixel color
out vec4 FragColor;
void main() {
vec4 tColor = tilesetGetColor(uint(TILESET_SLOT_ENTITIES), v_TextureCoord);
if(tColor.a == 0.0) discard;
if(tColor.r == 0.0) discard;
FragColor = vec4(1, 1, 1, 1) * tColor;
}

View File

@ -1,22 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../fragments/header.glsl"
#include "../data/transforms.glsl"
#include "../data/entities.glsl"
// Outputs to fragment shader
out vec2 v_TextureCoord;
void main() {
uint instanceIndex = uint(gl_InstanceID);
uint indiceIndex = quadGetIndiceIndex(gl_VertexID);
vec2 vert = entityGetVertice(instanceIndex, indiceIndex);
vec2 uv = entityGetUV(instanceIndex, indiceIndex);
gl_Position = transforms.projection * transforms.view * vec4(vert, 0.0, 1.0);
v_TextureCoord = uv;
}

View File

@ -1,64 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "assert/assertgl.h"
#include "util/memory.h"
#include "entityshader.h"
#include "entity_vert.glsl.h"
#include "entity_frag.glsl.h"
#include "display/shader/data/transforms.h"
#include "display/shader/data/entities.h"
#include "display/shader/data/tilesetshaderdata.h"
entityshader_t ENTITY_SHADER;
void entityShaderInit() {
memoryZero(&ENTITY_SHADER, sizeof(entityshader_t));
shaderInit(
&ENTITY_SHADER.shader,
entity_vertShaderSource,
entity_fragShaderSource
);
// Uniform buffers
ENTITY_SHADER.transformsBlock = shaderGetBlock(
&ENTITY_SHADER.shader,
TRANSFORMS_BLOCK_NAME
);
ENTITY_SHADER.entitiesBlock = shaderGetBlock(
&ENTITY_SHADER.shader,
ENTITIES_BLOCK_NAME
);
ENTITY_SHADER.tilesetsBlock = shaderGetBlock(
&ENTITY_SHADER.shader,
TILESET_SHADER_DATA_BLOCK_NAME
);
// Uniforms
ENTITY_SHADER.tilesetTexturesUniform = shaderGetUniform(
&ENTITY_SHADER.shader,
TILESET_UNIFORM_TEXTURES_NAME
);
}
void entityShaderUse() {
shaderUse(&ENTITY_SHADER.shader);
shaderBufferBindToBlock(&TRANSFORMS_BUFFER, ENTITY_SHADER.transformsBlock);
shaderBufferBindToBlock(&ENTITIES_BUFFER, ENTITY_SHADER.entitiesBlock);
shaderBufferBindToBlock(
&TILESET_SHADER_DATA_BUFFER, ENTITY_SHADER.tilesetsBlock
);
tilesetShaderTexturesBind(ENTITY_SHADER.tilesetTexturesUniform);
}
void entityShaderDispose() {
shaderDispose(&ENTITY_SHADER.shader);
}

View File

@ -1,36 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/shader/shader.h"
typedef struct {
shader_t shader;
GLuint entitiesBlock;
GLuint transformsBlock;
GLuint tilesetsBlock;
GLuint tilesetTexturesUniform;
} entityshader_t;
extern entityshader_t ENTITY_SHADER;
/**
* Initializes the entity shader.
*/
void entityShaderInit();
/**
* Uses the entity shader.
*/
void entityShaderUse();
/**
* Disposes of the entity shader.
*/
void entityShaderDispose();

View File

@ -1,9 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
)

View File

@ -1,6 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#version 330 core

View File

@ -1,39 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#define PACKED_U8_PER_UI 4
#define PACKED_U8_PER_UVEC4 PACKED_U8_PER_UI * 4
uint packedGetU8(uint position, uvec4 data) {
uint subData = data[position / 4u];
return (subData >> (position * 8u)) & 0xFFu;
}
uint packedGetU32(uint position, uvec4 data) {
return data[position];
}
int packedGetI8(uint position, uvec4 data) {
uint subData = data[position / 4u];
int shift = int(position * 8u);
return int(subData << (24 - shift)) >> 24;
}
uint packedArrayGetU8IndexFromUVEC4Array(uint u8ArrayIndex) {
// Given a uint8_t array is uploaded, this will return the index to get the
// appropriate uvec4 from a uvec4 array that will be at the right index.
return u8ArrayIndex / uint(PACKED_U8_PER_UVEC4);
}
uint packedArrayGetU8FromUVEC4ArrayValue(uint u8ArrayIndex, uvec4 data) {
// Given a value from a uint8_t array, this will return the value at the
// appropriate index. You must first get the uvec4 from the array using
// packedArrayGetU8IndexFromUVEC4Array.
uint subIndex = (u8ArrayIndex % uint(PACKED_U8_PER_UVEC4)) / uint(PACKED_U8_PER_UI);
uint shiftAmount = (u8ArrayIndex % uint(PACKED_U8_PER_UI)) * 8u;
uint value = (data[subIndex] >> shiftAmount) & 0xFFu;
return value;
}

View File

@ -1,39 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
uint quadGetIndiceIndex(uint vertexId) {
return vertexId % 6u;
}
uint quadGetIndiceIndex(int vertexId) {
return quadGetIndiceIndex(uint(vertexId));
}
vec2 quadGetVertice(uint indiceIndex) {
vec2 vert = vec2(0, 0);
/*if(indiceIndex == 0u || indiceIndex == 4u) {
// vert = vec2(0, 0);
} else*/ if(indiceIndex == 1u) {
vert = vec2(1, 0);
} else if(indiceIndex == 2u || indiceIndex == 5u) {
vert = vec2(1, 1);
} else if(indiceIndex == 3u) {
vert = vec2(0, 1);
}
return vert;
}
vec2 quadGetTextureCoordinate(uint indiceIndex) {
vec2 vert = quadGetVertice(indiceIndex);
return vert;
}
vec2 quadGetTextureCoordinate(uint indiceIndex, vec4 uv) {
vec2 vert = quadGetVertice(indiceIndex);
vert.y = 1.0 - vert.y;
return vec2(uv.x + (uv.z - uv.x) * vert.x, uv.y + (uv.w - uv.y) * vert.y);
}

View File

@ -1,14 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Shaders
glsltool(map_vert.glsl)
glsltool(map_frag.glsl)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
mapshader.c
)

View File

@ -1,28 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../fragments/header.glsl"
#include "../data/map.glsl"
// Inputs from vertex shader
in vec2 v_TextureCoord;
flat in uint v_InstanceIndex;
flat in uint v_IndiceIndex;
// Frag pixel color
out vec4 FragColor;
void main() {
uint layer = mapGetLayer(v_InstanceIndex);
uint tileId = mapGetTileId(v_InstanceIndex);
if(tileId == 0u) {
FragColor = vec4(1, 0, 0, 1);
} else if(tileId == 1u) {
FragColor = vec4(0, 1, 0, 1);
} else {
FragColor = vec4(0, 0, 1, 1);
}
}

View File

@ -1,28 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "../fragments/header.glsl"
#include "../data/transforms.glsl"
#include "../data/map.glsl"
// Outputs to fragment shader
out vec2 v_TextureCoord;
flat out uint v_InstanceIndex;
flat out uint v_IndiceIndex;
void main() {
uint instanceIndex = uint(gl_InstanceID);
uint indiceIndex = quadGetIndiceIndex(gl_VertexID);
uint layer = mapGetLayer(instanceIndex);
vec2 vert = mapGetVertice(instanceIndex, indiceIndex);
vec2 uv = quadGetTextureCoordinate(indiceIndex);
gl_Position = transforms.projection * transforms.view * vec4(vert, float(layer) * 64.0, 1.0);
v_TextureCoord = uv;
v_InstanceIndex = instanceIndex;
v_IndiceIndex = indiceIndex;
}

View File

@ -1,46 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mapshader.h"
#include "util/memory.h"
#include "display/shader/data/mapshaderdata.h"
#include "display/shader/data/transforms.h"
#include "map_vert.glsl.h"
#include "map_frag.glsl.h"
mapshader_t MAP_SHADER;
void mapShaderInit() {
memoryZero(&MAP_SHADER, sizeof(mapshader_t));
shaderInit(
&MAP_SHADER.shader,
map_vertShaderSource,
map_fragShaderSource
);
// Uniform blocks
MAP_SHADER.mapBlock = shaderGetBlock(
&MAP_SHADER.shader,
MAP_BLOCK_NAME
);
MAP_SHADER.transformsBlock = shaderGetBlock(
&MAP_SHADER.shader,
TRANSFORMS_BLOCK_NAME
);
}
void mapShaderUse() {
shaderUse(&MAP_SHADER.shader);
shaderBufferBindToBlock(&MAP_SHADER_DATA_BUFFER, MAP_SHADER.mapBlock);
shaderBufferBindToBlock(&TRANSFORMS_BUFFER, MAP_SHADER.transformsBlock);
}
void mapShaderDispose() {
shaderDispose(&MAP_SHADER.shader);
}

View File

@ -1,32 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/shader/shader.h"
typedef struct {
shader_t shader;
GLuint mapBlock;
GLuint transformsBlock;
} mapshader_t;
extern mapshader_t MAP_SHADER;
/**
* Initializes the map shader.
*/
void mapShaderInit();
/**
* Uses the map shader.
*/
void mapShaderUse();
/**
* Destroys the map shader.
*/
void mapShaderDispose();

View File

@ -1,146 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "shader.h"
#include "assert/assert.h"
#include "assert/assertgl.h"
void shaderInit(
shader_t *shader,
const char *vertexSource,
const char *fragmentSource
) {
assertNotNull(shader, "shader must not be NULL");
assertNotNull(vertexSource, "vertexSource must not be NULL");
assertNotNull(fragmentSource, "fragmentSource must not be NULL");
int32_t success;
char infoLog[SHADER_LOG_LENGTH];
// Create vertex shader
shader->vertexShader = glCreateShader(GL_VERTEX_SHADER);
assertNoGLError();
glShaderSource(shader->vertexShader, 1, &vertexSource, NULL);
assertNoGLError();
glCompileShader(shader->vertexShader);
assertNoGLError();
glGetShaderiv(shader->vertexShader, GL_COMPILE_STATUS, &success);
assertNoGLError();
if(!success) {
glGetShaderInfoLog(
shader->vertexShader, SHADER_LOG_LENGTH, NULL, infoLog
);
assertNoGLError();
assertUnreachable(infoLog);
return;
}
// Create fragment shader
shader->fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
assertNoGLError();
glShaderSource(shader->fragmentShader, 1, &fragmentSource, NULL);
assertNoGLError();
glCompileShader(shader->fragmentShader);
assertNoGLError();
glGetShaderiv(shader->fragmentShader, GL_COMPILE_STATUS, &success);
assertNoGLError();
if(!success) {
glGetShaderInfoLog(
shader->fragmentShader, SHADER_LOG_LENGTH, NULL, infoLog
);
assertNoGLError();
assertUnreachable(infoLog);
return;
}
// Create shader program
shader->shaderProgram = glCreateProgram();
assertNoGLError();
glAttachShader(shader->shaderProgram, shader->vertexShader);
assertNoGLError();
glAttachShader(shader->shaderProgram, shader->fragmentShader);
assertNoGLError();
glLinkProgram(shader->shaderProgram);
assertNoGLError();
glGetProgramiv(shader->shaderProgram, GL_LINK_STATUS, &success);
assertNoGLError();
if(!success) {
glGetProgramInfoLog(shader->shaderProgram, SHADER_LOG_LENGTH, NULL, infoLog);
assertNoGLError();
assertUnreachable(infoLog);
return;
}
}
void shaderUse(const shader_t *shader) {
assertNotNull(shader, "shader must not be NULL");
glUseProgram(shader->shaderProgram);
assertNoGLError();
}
GLuint shaderGetUniform(const shader_t *shader, const char_t *name) {
assertNotNull(shader, "shader must not be NULL");
assertNotNull(name, "name must not be NULL");
GLuint uniform = glGetUniformLocation(shader->shaderProgram, name);
assertNoGLError();
return uniform;
}
GLuint shaderGetBlock(const shader_t *shader, const char *name) {
assertNotNull(shader, "shader must not be NULL");
assertNotNull(name, "name must not be NULL");
GLuint blockIndex = glGetUniformBlockIndex(shader->shaderProgram, name);
assertNoGLError();
// TODO: I really don't think this should be here at all.
glUniformBlockBinding(shader->shaderProgram, blockIndex, blockIndex);
assertNoGLError();
return blockIndex;
}
void shaderSetTextures(
const GLuint uniform,
const GLuint *textures,
const uint8_t count
) {
assertNotNull(textures, "textures must not be NULL");
assertTrue(count > 0, "count must be greater than 0");
glUniform1iv(uniform, count, textures);
assertNoGLError();
}
void shaderDispose(shader_t *shader) {
assertNotNull(shader, "shader must not be NULL");
glDeleteProgram(shader->shaderProgram);
assertNoGLError();
glDeleteShader(shader->vertexShader);
assertNoGLError();
glDeleteShader(shader->fragmentShader);
assertNoGLError();
}

View File

@ -1,75 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
#define SHADER_LOG_LENGTH 512
typedef struct {
GLuint shaderProgram;
GLuint vertexShader;
GLuint fragmentShader;
} shader_t;
/**
* Initializes a shader.
*
* @param shader The shader to initialize.
* @param vertexSource The vertex shader source.
* @param fragmentSource The fragment shader source.
*/
void shaderInit(
shader_t *shader,
const char_t *vertexSource,
const char_t *fragmentSource
);
/**
* Uses a shader.
*
* @param shader The shader to use.
*/
void shaderUse(const shader_t *shader);
/**
* Gets a uniform from a shader.
*
* @param shader The shader to get the uniform from.
* @param name The name of the uniform.
* @return The uniform.
*/
GLuint shaderGetUniform(const shader_t *shader, const char_t *name);
/**
* Gets a block id from a shader.
*
* @param shader The shader to get the block from.
* @param name The name of the block.
* @return The block index/identifier.
*/
GLuint shaderGetBlock(const shader_t *shader, const char_t *name);
/**
* Sets texture uniforms to a shader.
*
* @param uniform The uniform to set.
* @param textures The textures to set.
* @param count The number of textures to set.
*/
void shaderSetTextures(
const GLuint uniform,
const GLuint *textures,
const uint8_t count
);
/**
* Disposes of a shader.
*
* @param shader The shader to dispose of.
*/
void shaderDispose(shader_t *shader);

View File

@ -1,55 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "shaderbuffer.h"
#include "assert/assertgl.h"
void shaderBufferInit(shaderbuffer_t *shaderBuffer, const size_t size) {
assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL.");
assertTrue(size > 0, "size must be greater than 0.");
shaderBuffer->size = size;
glGenBuffers(1, &shaderBuffer->id);
assertNoGLError();
shaderBufferBind(shaderBuffer);
glBufferData(GL_UNIFORM_BUFFER, size, NULL, GL_STATIC_DRAW);
}
void shaderBufferBind(shaderbuffer_t *shaderBuffer) {
assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL.");
glBindBuffer(GL_UNIFORM_BUFFER, shaderBuffer->id);
assertNoGLError();
}
void shaderBufferSetData(shaderbuffer_t *shaderBuffer, const void *data) {
assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL.");
assertNotNull(data, "data cannot be NULL.");
glBufferData(GL_UNIFORM_BUFFER, shaderBuffer->size, data, GL_STATIC_DRAW);
assertNoGLError();
}
void shaderBufferBindToBlock(
shaderbuffer_t *shaderBuffer,
const GLuint blockIndex
) {
assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL.");
glBindBufferBase(GL_UNIFORM_BUFFER, blockIndex, shaderBuffer->id);
assertNoGLError();
}
void shaderBufferDispose(shaderbuffer_t *shaderBuffer) {
assertNotNull(shaderBuffer, "shaderBuffer cannot be NULL.");
glDeleteBuffers(1, &shaderBuffer->id);
assertNoGLError();
}

View File

@ -1,61 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
typedef struct {
GLuint id;
size_t size;
} shaderbuffer_t;
/**
* Initializes a shader buffer.
*
* @param shaderBuffer The shader buffer to initialize.
* @param size The size of the buffer.
*/
void shaderBufferInit(
shaderbuffer_t *shaderBuffer,
const size_t size
);
/**
* Binds a shader buffer.
*
* @param shaderBuffer The shader buffer to bind.
*/
void shaderBufferBind(shaderbuffer_t *shaderBuffer);
/**
* Sets the data of a shader buffer.
*
* @param shaderBuffer The shader buffer to set the data of.
* @param data The data to set.
*/
void shaderBufferSetData(
shaderbuffer_t *shaderBuffer,
const void *data
);
/**
* Binds a shader buffer to a block.
*
* @param shaderBuffer The shader buffer to bind.
* @param blockIndex The block index to bind to.
*/
void shaderBufferBindToBlock(
shaderbuffer_t *shaderBuffer,
const GLuint blockIndex
);
/**
* Disposes of a shader buffer.
*
* @param shaderBuffer The shader buffer to dispose of.
*/
void shaderBufferDispose(shaderbuffer_t *shaderBuffer);

View File

@ -1,109 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "shadermanager.h"
#include "assert/assert.h"
#include "display/shader/data/transforms.h"
#include "display/shader/data/entities.h"
#include "display/shader/data/mapshaderdata.h"
#include "display/shader/data/tilesetshaderdata.h"
#include "display/shader/entityshader/entityshader.h"
#include "display/shader/mapshader/mapshader.h"
shadermanagerdatacallback_t SHADER_MANAGER_DATA_CALLBACKS[] = {
{ transformsInit, transformsUpdate, transformsDispose },
{ entitiesInit, entitiesUpdate, entitiesDispose },
{ mapShaderDataInit, mapShaderDataUpdate, mapShaderDataDispose },
{ tilesetShaderDataInit, tilesetShaderDataUpdate, tilesetShaderDataDispose }
};
shadermanagershadercallback_t SHADER_MANAGER_SHADER_CALLBACKS[] = {
{ entityShaderInit, entityShaderDispose },
{ mapShaderInit, mapShaderDispose }
};
void shaderManagerInit() {
size_t i;
assertTrue(
SHADER_MANAGER_SHADER_CALLBACKS_SIZE > 0,
"No Shader Callbacks Defined"
);
assertTrue(
SHADER_MANAGER_DATA_CALLBACKS_SIZE > 0,
"No Data Callbacks Defined"
);
// Init Shader Datas (before the shaders)
i = 0;
do {
assertNotNull(
SHADER_MANAGER_DATA_CALLBACKS[i].init,
"Data Callback is NULL"
);
SHADER_MANAGER_DATA_CALLBACKS[i++].init();
} while(i < SHADER_MANAGER_DATA_CALLBACKS_SIZE);
// Init the shaders
i = 0;
do {
assertNotNull(
SHADER_MANAGER_SHADER_CALLBACKS[i].init,
"Shader Callback is NULL"
);
SHADER_MANAGER_SHADER_CALLBACKS[i++].init();
} while(i < SHADER_MANAGER_SHADER_CALLBACKS_SIZE);
}
void shaderManagerUpdate() {
assertTrue(
SHADER_MANAGER_DATA_CALLBACKS_SIZE > 0,
"No Data Callbacks Defined"
);
// Update all the data
size_t i = 0;
do {
assertNotNull(
SHADER_MANAGER_DATA_CALLBACKS[i].update,
"Data Callback is NULL"
);
SHADER_MANAGER_DATA_CALLBACKS[i++].update();
} while(i < SHADER_MANAGER_DATA_CALLBACKS_SIZE);
}
void shaderManagerDispose() {
assertTrue(
SHADER_MANAGER_SHADER_CALLBACKS_SIZE > 0,
"No Shader Callbacks Defined"
);
assertTrue(
SHADER_MANAGER_DATA_CALLBACKS_SIZE > 0,
"No Data Callbacks Defined"
);
// Cleanup the shaders
size_t i = 0;
do {
assertNotNull(
SHADER_MANAGER_SHADER_CALLBACKS[i].dispose,
"Shader Callback is NULL"
);
SHADER_MANAGER_SHADER_CALLBACKS[i++].dispose();
} while(i < SHADER_MANAGER_SHADER_CALLBACKS_SIZE);
// Cleanup the data
i = 0;
do {
assertNotNull(
SHADER_MANAGER_DATA_CALLBACKS[i].dispose,
"Data Callback is NULL"
);
SHADER_MANAGER_DATA_CALLBACKS[i++].dispose();
} while(i < SHADER_MANAGER_DATA_CALLBACKS_SIZE);
}

View File

@ -1,47 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct {
void (*init)();
void (*update)();
void (*dispose)();
} shadermanagerdatacallback_t;
typedef struct {
void (*init)();
void (*dispose)();
} shadermanagershadercallback_t;
extern shadermanagerdatacallback_t SHADER_MANAGER_DATA_CALLBACKS[];
extern shadermanagershadercallback_t SHADER_MANAGER_SHADER_CALLBACKS[];
#define SHADER_MANAGER_DATA_CALLBACKS_SIZE ( \
sizeof(SHADER_MANAGER_DATA_CALLBACKS) / sizeof(shadermanagerdatacallback_t) \
)
#define SHADER_MANAGER_SHADER_CALLBACKS_SIZE ( \
sizeof(SHADER_MANAGER_SHADER_CALLBACKS) / \
sizeof(shadermanagershadercallback_t) \
)
/**
* Initializes the Shader Manager
*/
void shaderManagerInit();
/**
* Updates the Shader Manager
*/
void shaderManagerUpdate();
/**
* Disposes of the Shader Manager
*/
void shaderManagerDispose();

View File

@ -1,103 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "texture.h"
#include "assert/assert.h"
#include "assert/assertgl.h"
#include "asset.h"
#include "util/math.h"
#include "util/memory.h"
#define TEXTURE_BUFFER_SIZE 32768
int32_t TEXTURE_ACTIVE_COUNT;
void textureLoad(
texture_t *texture,
const char_t *path
) {
assertNotNull(texture, "Texture is null.");
assertNotNull(path, "Path is null.");
// Open asset
assetOpen(path);
// Setup spng
spng_ctx *ctx = spng_ctx_new(0);
spng_set_png_stream(ctx, &textureSPNGBuffer, NULL);
// Get image info
struct spng_ihdr ihdr;
spng_get_ihdr(ctx, &ihdr);
texture->width = ihdr.width;
texture->height = ihdr.height;
// Decode the image. I can probably stream this in the future.
size_t dataSize = ihdr.width * ihdr.height * 4;// 4 for RGBA
uint8_t *data = (uint8_t *)memoryAllocate(dataSize);
assertNotNull(data, "Failed to allocate memory for texture data.");
spng_decode_image(ctx, data, dataSize, SPNG_FMT_RGBA8, 0);
// Finish decoding
spng_ctx_free(ctx);
assetClose();
// Create texture
glGenTextures(1, &texture->id);
assertNoGLError();
glBindTexture(GL_TEXTURE_2D, texture->id);
assertNoGLError();
// Buffer then cleanup
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA,
texture->width, texture->height,
0, GL_RGBA, GL_UNSIGNED_BYTE, data
);
assertNoGLError();
memoryFree(data);
// Setup texture parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
assertNoGLError();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
assertNoGLError();
glBindTexture(GL_TEXTURE_2D, 0);
assertNoGLError();
}
void textureBind(
const texture_t *texture,
const GLuint slot
) {
assertNotNull(texture, "Texture is null.");
glActiveTexture(GL_TEXTURE0 + slot);
assertNoGLError();
glBindTexture(GL_TEXTURE_2D, texture->id);
assertNoGLError();
}
void textureDispose(texture_t *texture) {
glDeleteTextures(1, &texture->id);
assertNoGLError();
}
int32_t textureSPNGBuffer(
spng_ctx *ctx,
void *user,
void *destination,
size_t length
) {
size_t read = assetRead(destination, length);
if(read == 0) return SPNG_IO_EOF;
return SPNG_OK;
}

View File

@ -1,63 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"
#include <spng.h>
extern int32_t TEXTURE_ACTIVE_COUNT;
typedef struct {
uint32_t width;
uint32_t height;
GLuint id;
} texture_t;
/**
* Initializes a texture to be read to be used.
*
* @param texture Texture to initialize.
* @param path Path of the file to load.
*/
void textureLoad(
texture_t *texture,
const char_t *path
);
/**
* Binds the texture to the given slot (for use by the shaders).
*
* @param texture Texture to bind.
* @param slot Slot to bind to.
*/
void textureBind(
const texture_t *texture,
const GLuint slot
);
/**
* Unloads a previously initialized texture.
*
* @param texture Texture to destroy.
*/
void textureDispose(texture_t *texture);
/**
* Callback function for libspng to read from the asset.
*
* @param ctx The spng context.
* @param user User data.
* @param destination Destination buffer.
* @param length Length of the buffer.
* @return The amount of data read.
*/
int32_t textureSPNGBuffer(
spng_ctx *ctx,
void *user,
void *destination,
size_t length
);

View File

@ -1,32 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "tilesetgl.h"
#include "assert/assert.h"
const char* TILESET_GL_TEXTURES_PATHS[TILESET_COUNT] = {
NULL,
"textures/8x8.png"
};
texture_t TILESET_GL_TEXTURES[TILESET_SLOT_COUNT];
void tilesetGLBind() {
uint8_t i;
do {
if(TILESET_SLOTS[i] == TILESET_NULL) continue;
textureBind(TILESET_GL_TEXTURES + i, i);
} while(++i < TILESET_SLOT_COUNT);
}
void tilesetBind(const tilesetid_t id, const uint8_t slot) {
assertTrue(slot < TILESET_SLOT_COUNT, "Invalid slot");
assertTrue(id < TILESET_COUNT, "Invalid tileset id");
TILESET_SLOTS[slot] = id;
textureLoad(TILESET_GL_TEXTURES + slot, TILESET_GL_TEXTURES_PATHS[id]);
}

View File

@ -1,18 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "texture.h"
#include "display/tileset.h"
extern const char* TILESET_GL_TEXTURES_PATHS[];
extern texture_t TILESET_GL_TEXTURES[];
/**
* Binds the tileset to the OpenGL context.
*/
void tilesetGLBind();

View File

@ -1,24 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "duskglimpl.h"
#include <libgen.h>
#include <cglm/cglm.h>
#include <float.h>
#include <pthread.h>
#include <unistd.h>
typedef float float_t;
typedef double double_t;
extern char_t EXECUTABLE_PATH[];
extern char_t EXECUTABLE_DIRECTORY[];
typedef uint32_t uvec4_t[4];

View File

@ -1,12 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
overworld.c
)
# Subdirs

View File

@ -1,21 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworld/overworld.h"
#include "display/quad.h"
#include "display/shader/entityshader/entityshader.h"
#include "display/shader/mapshader/mapshader.h"
void overworldRender() {
mapShaderUse();
quadRender(
OVERWORLD.map.width * OVERWORLD.map.height * OVERWORLD.map.layerCount
);
entityShaderUse();
quadRender(OVERWORLD.entityCount);
}

View File

@ -1,27 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
glfw
glad
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
main.c
input.c
)
# Subdirs
add_subdirectory(display)

View File

@ -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_TARGET_NAME}
PRIVATE
window.c
)

View File

@ -1,79 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "window.h"
GLFWwindow* window;
uint32_t WINDOW_WIDTH;
uint32_t WINDOW_HEIGHT;
int32_t windowInit() {
// Initialize GLFW
if(!glfwInit()) {
const char* description;
int code = glfwGetError(&description);
if (description) {
printf("GLFW Error %d: %s\n", code, description);
} else {
printf("GLFW Error: Unknown error\n");
}
return -1;
}
// Create a windowed mode window and its OpenGL contex
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
glfwWindowHint(GLFW_MAXIMIZED, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
window = glfwCreateWindow(
WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT,
"Dusk",
NULL, NULL
);
WINDOW_WIDTH = WINDOW_WIDTH_DEFAULT;
WINDOW_HEIGHT = WINDOW_HEIGHT_DEFAULT;
if(!window) {
glfwTerminate();
return -2;
}
// Make the window's context current
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
// Get initial framebuffer sizeglfwCreateWindow
glfwGetFramebufferSize(window, &WINDOW_WIDTH, &WINDOW_HEIGHT);
// Load OpenGL functions using glad or another loader here if necessary
if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) return -3;
return 0;
}
bool_t windowShouldClose() {
return glfwWindowShouldClose(window);
}
void windowUpdate() {
glfwSwapBuffers(window);
glfwPollEvents();
glfwGetFramebufferSize(window, &WINDOW_WIDTH, &WINDOW_HEIGHT);
}
void windowDispose() {
glfwDestroyWindow(window);
glfwTerminate();
}

View File

@ -1,41 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskglfw.h"
#include "display/displaydefs.h"
#define WINDOW_WIDTH_DEFAULT SCREEN_WIDTH*3
#define WINDOW_HEIGHT_DEFAULT SCREEN_HEIGHT*3
extern GLFWwindow* window;
extern uint32_t WINDOW_WIDTH;
extern uint32_t WINDOW_HEIGHT;
/**
* Initializes the game render window.
*
* @return 0 on success, any other value on failure.
*/
int32_t windowInit();
/**
* Returns whether the window should close.
*
* @return true if the window should close, false otherwise.
*/
bool_t windowShouldClose();
/**
* Updates the window subsystem.
*/
void windowUpdate();
/**
* Disposes of the window.
*/
void windowDispose();

View File

@ -1,9 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "duskgl.h"

View File

@ -1,11 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include <glad/glad.h>
#include <GLFW/glfw3.h>

View File

@ -1,56 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
#include "display/window.h"
typedef struct {
uint16_t glfw;
inputstate_t bind;
} glfwkeybindmap_t;
glfwkeybindmap_t GLFW_KEY_BIND_MAP[] = {
{GLFW_KEY_W, INPUT_UP},
{GLFW_KEY_UP, INPUT_UP},
{GLFW_KEY_S, INPUT_DOWN},
{GLFW_KEY_DOWN, INPUT_DOWN},
{GLFW_KEY_A, INPUT_LEFT},
{GLFW_KEY_LEFT, INPUT_LEFT},
{GLFW_KEY_D, INPUT_RIGHT},
{GLFW_KEY_RIGHT, INPUT_RIGHT},
{GLFW_KEY_ENTER, INPUT_ACCEPT},
{GLFW_KEY_E, INPUT_ACCEPT},
{GLFW_KEY_SPACE, INPUT_ACCEPT},
{GLFW_KEY_BACKSPACE, INPUT_BACK},
{GLFW_KEY_Q, INPUT_BACK},
{GLFW_KEY_ESCAPE, INPUT_MENU},
{0, 0}
};
inputstate_t inputPlatformState() {
inputstate_t state = 0;
// Handle keybinds
glfwkeybindmap_t *map = GLFW_KEY_BIND_MAP;
do {
if(glfwGetKey(window, map->glfw) != GLFW_PRESS) {
map++;
continue;
}
state |= map->bind;
map++;
} while(map->glfw != 0);
return state;
}

View File

@ -1,70 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "display/window.h"
#include "display/render.h"
#include "game.h"
#include "asset.h"
char_t EXECUTABLE_PATH[FILENAME_MAX];
char_t EXECUTABLE_DIRECTORY[FILENAME_MAX];
int32_t main(int32_t argc, char_t **argv) {
// Get launch args
assertTrue(argc > 0, "argc is not valid!");
assertNotNull(argv, "argv is not valid!");
assertNotNull(argv[0], "argv[0] is not valid!");
assertStrLen(argv[0], FILENAME_MAX, "argv[0] is too long!");
assertStrLenMin(argv[0], 1, "argv[0] is empty!");
// Store the executable path and directory.
strcpy(EXECUTABLE_PATH, argv[0]);
strcpy(EXECUTABLE_DIRECTORY, dirname(EXECUTABLE_PATH));
// Init systems
int32_t ret;
if((ret = windowInit()) != 0) {
return -1;
}
gameInit();
// Init asset and render systems.
assetInit();
renderInit();
// Prepare for time tracking
double_t time, newTime;
float_t fDelta, fTimeSinceLastFrame = 1.0f;
int32_t updateResult;
// Main loop
while(!windowShouldClose()) {
// Determine the delta.
newTime = glfwGetTime();
fDelta = (float_t)(newTime - time);
time = newTime;
fTimeSinceLastFrame += fDelta;
// Tick according to the game tick rate.
if(fTimeSinceLastFrame >= (1.0f / GAME_TICK_RATE)) {
gameUpdate();
fTimeSinceLastFrame = 0.0f;
}
// Draw. In future may be tick based.
renderUpdate();
windowUpdate();
}
renderDispose();
windowDispose();
assetDispose();
gameDispose();
return 0;
}

View File

@ -1,23 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
main.c
)
# Subdirs

View File

@ -1,39 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusk.h"
#include "error/error.h"
#include "console/console.h"
#include "util/string.h"
bool_t exitRequested = false;
void cmdExit(const consolecmdexec_t *exec) {
exitRequested = true;
}
void testChange(const consolevar_t *var) {
consolePrint("Variable '%s' changed to '%s'.", var->name, var->value);
}
int main() {
consoleInit();
consoleRegCmd("exit", cmdExit);
consoleRegVar("test", "Hello", testChange);
consolePrint(" = Dusk Console = ");
char_t buffer[CONSOLE_LINE_MAX * 10];
while(fgets(buffer, sizeof(buffer), stdin)) {
consoleExec(buffer);
consoleProcess();
if(exitRequested) break;
}
printf("Goodbye!\n");
return 0;
}

View File

@ -35,11 +35,21 @@ bool_t errorCheck() {
return ERROR_STACK.code != ERROR_OK; return ERROR_STACK.code != ERROR_OK;
} }
errorret_t errorPrint() { const char_t * errorString() {
if(ERROR_STACK.code == ERROR_OK) return ERROR_OK; if(!errorCheck()) return NULL;
return ERROR_STACK.message;
}
printf("Error: %s\n", ERROR_STACK.message); void errorFlush() {
errorret_t code = ERROR_STACK.code; if(!errorCheck()) return;
ERROR_STACK.code = ERROR_OK; ERROR_STACK.code = ERROR_OK;
ERROR_STACK.message[0] = '\0';
}
errorret_t errorPrint() {
if(!errorCheck()) return ERROR_OK;
printf("Error: %s\n", errorString());
errorret_t code = ERROR_STACK.code;
errorFlush();
return code; return code;
} }

View File

@ -44,6 +44,16 @@ errorret_t errorCode(const errorret_t code, const char_t *message, ...);
*/ */
bool_t errorCheck(); bool_t errorCheck();
/**
* Retrieves the error message from the error stack.
*/
const char_t * errorString();
/**
* Clears the error stack.
*/
void errorFlush();
/** /**
* Prints the error stack to the console. This also clears the error stack. * Prints the error stack to the console. This also clears the error stack.
*/ */

48
src/main.c Normal file
View File

@ -0,0 +1,48 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "console/console.h"
#include "server/server.h"
void cmdExit(const consolecmdexec_t *exec) {
CloseWindow();
}
void cmdServe(const consolecmdexec_t *exec) {
serverStart(3030);
}
void cmdClose(const consolecmdexec_t *exec) {
serverStop();
}
int main(void) {
consoleInit();
serverInit();
consoleRegCmd("exit", cmdExit);
consoleRegCmd("serve", cmdServe);
consoleRegCmd("close", cmdClose);
InitWindow(1280, 720, DUSK_NAME);
while(!WindowShouldClose()) {
consoleUpdate();
BeginDrawing();
ClearBackground(RAYWHITE);
consoleDraw();
EndDrawing();
}
CloseWindow();
serverDispose();
return EXIT_SUCCESS;
}

View File

@ -6,5 +6,6 @@
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME}
PRIVATE PRIVATE
assertgl.c server.c
serverclient.c
) )

192
src/server/server.c Normal file
View File

@ -0,0 +1,192 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "server.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "console/console.h"
server_t SERVER;
void serverInit() {
memoryZero(&SERVER, sizeof(server_t));
pthread_mutex_init(&SERVER.startMutex, NULL);
pthread_cond_init(&SERVER.startCond, NULL);
}
errorret_t serverStart(uint16_t port) {
assertFalse(SERVER.isRunning, "Server is already running?");
consolePrint("Starting server on port %d...\n", port);
pthread_mutex_lock(&SERVER.startMutex);
SERVER.threadReady = false;
// Initialize the server socket
SERVER.serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if(SERVER.serverSocket < 0) {
pthread_mutex_unlock(&SERVER.startMutex);
return error("Failed to create socket");
}
SERVER.serverAddress.sin_family = AF_INET;
SERVER.serverAddress.sin_addr.s_addr = INADDR_ANY;
SERVER.serverAddress.sin_port = htons(port);
// Bind the socket to the address and port
int32_t res = bind(
SERVER.serverSocket,
(struct sockaddr *)&SERVER.serverAddress,
sizeof(SERVER.serverAddress)
);
if(res < 0) {
close(SERVER.serverSocket);
pthread_mutex_unlock(&SERVER.startMutex);
return error("Failed to bind socket");
}
// Set the socket to listen for incoming connections
res = listen(SERVER.serverSocket, SERVER_MAX_CLIENTS);
if(res < 0) {
close(SERVER.serverSocket);
pthread_mutex_unlock(&SERVER.startMutex);
return error("Failed to listen on socket");
}
// Start the server thread
SERVER.isRunning = true;
if(pthread_create(&SERVER.thread, NULL, serverThread, NULL) != 0) {
perror("Failed to create server thread");
serverStop();
pthread_mutex_unlock(&SERVER.startMutex);
return error("Failed to create server thread");
}
// Wait for the server thread to be ready
while(!SERVER.threadReady) {
pthread_cond_wait(&SERVER.startCond, &SERVER.startMutex);
}
pthread_mutex_unlock(&SERVER.startMutex);
consolePrint("Server started.");
return ERROR_OK;
}
void * serverThread(void *arg) {
struct timeval timeout;
fd_set readfds;
pthread_mutex_lock(&SERVER.startMutex);
SERVER.threadReady = true;
pthread_cond_signal(&SERVER.startCond);
pthread_mutex_unlock(&SERVER.startMutex);
while(SERVER.isRunning) {
usleep(SERVER_TIMEOUT * 1000);
FD_ZERO(&readfds);
FD_SET(SERVER.serverSocket, &readfds);
timeout.tv_usec = SERVER_TIMEOUT * 1000;
// Wait for
int32_t activity = select(
SERVER.serverSocket + 1,
&readfds,
NULL,
NULL,
&timeout
);
// Check for errors
if(activity < 0) {
consolePrint("Error in select");
continue;
}
// Timeout
if(activity == 0) continue;
// Check if there is a new connection
if(!FD_ISSET(SERVER.serverSocket, &readfds)) continue;
// Accept new connection
int32_t clientSocket = accept(SERVER.serverSocket, NULL, NULL);
if(clientSocket < 0) {
consolePrint("Failed to accept connection");
continue;
}
// Initialize the client
serverclient_t *client = NULL;
uint8_t i = 0;
do {
if(SERVER.clients[i].state != SERVER_CLIENT_STATE_DISCONNECTED) {
i++;
continue;
}
client = &SERVER.clients[i];
break;
} while(i < SERVER_MAX_CLIENTS);
// Can we receive this client?
if(!client) {
const char_t *errMsg = "ERR|Server full";
send(clientSocket, errMsg, strlen(errMsg), 0);
consolePrint("Server full, closing connection.");
close(clientSocket);
continue;
}
errorret_t ret = serverClientAccept(client, clientSocket);
if(ret != ERROR_OK) {
consolePrint("Failed to accept client connection", errorString());
errorFlush();
close(clientSocket);
}
usleep(1000);
}
printf("Server thread exiting.\n");
assertFalse(
SERVER.isRunning, "Server thread exiting while server is running?"
);
return NULL;
}
uint8_t serverGetClientCount() {
uint8_t i = 0, clientCount = 0;
do {
if(SERVER.clients[i].state == SERVER_CLIENT_STATE_DISCONNECTED) continue;
clientCount++;
} while(i < SERVER_MAX_CLIENTS);
return clientCount;
}
void serverStop() {
consolePrint("Stopping server...");
SERVER.isRunning = false;
if(SERVER.serverSocket >= 0) {
close(SERVER.serverSocket);
SERVER.serverSocket = -1;
}
if(SERVER.thread) {
pthread_join(SERVER.thread, NULL);
SERVER.thread = 0;
}
pthread_mutex_destroy(&SERVER.startMutex);
pthread_cond_destroy(&SERVER.startCond);
consolePrint("Server stopped.");
}
void serverDispose() {
serverStop();
}

81
src/server/server.h Normal file
View File

@ -0,0 +1,81 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "serverclient.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_MAX_CLIENTS 32
#define SERVER_MAX_CHANNELS 2
#define SERVER_TIMEOUT 200
typedef struct {
bool_t isRunning;
pthread_t thread;
int serverSocket;
struct sockaddr_in serverAddress;
pthread_mutex_t startMutex;
pthread_cond_t startCond;
bool_t threadReady;
serverclient_t clients[SERVER_MAX_CLIENTS];
} server_t;
extern server_t SERVER;
/**
* Initialize the server
*
* This function initializes the server by setting up the ENet library and
* creating a server host.
*/
void serverInit();
/**
* Start the server
*
* This function starts the server by creating a host, binding it to the
* specified address and port, and starting the server thread.
*
* @param port The port number to bind the server to.
* @return Returns an error code if the server fails to start.
*/
errorret_t serverStart(uint16_t port);
/**
* Server thread function
*
* This function runs in a separate thread and handles incoming connections,
* messages, and disconnections.
*
* @param arg Pointer to the argument passed to the thread.
*/
void * serverThread(void *arg);
/**
* Get the client count
*
* This function returns the number of connected clients to the server.
*
* @return The number of connected clients.
*/
uint8_t serverGetClientCount();
/**
* Stop the server
*
* This function stops the server by destroying the host and cleaning up
* resources.
*/
void serverStop();
/**
* Dispose of the server
*
* This function disposes of the server by deinitializing the ENet library.
*/
void serverDispose();

109
src/server/serverclient.c Normal file
View File

@ -0,0 +1,109 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "serverclient.h"
#include "util/memory.h"
#include <sys/socket.h>
#include "console/console.h"
errorret_t serverClientAccept(
serverclient_t *client,
const int32_t socket
) {
memoryZero(client, sizeof(serverclient_t));
client->socket = socket;
client->state = SERVER_CLIENT_STATE_ACCEPTING;
// Set timeout to 20 seconds
client->timeout.tv_usec = SERVER_CLIENT_TIMEOUT * 1000;
// Create a thread for the client
if(pthread_create(&client->thread, NULL, serverClientThread, client) != 0) {
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
return error("Failed to create client thread");
}
}
errorret_t serverClientSend(
serverclient_t * client,
const uint8_t *data,
const size_t len
) {
if(client->state != SERVER_CLIENT_STATE_ACCEPTING) {
return error("Client is not accepting");
}
ssize_t sent = send(client->socket, data, len, 0);
if(sent < 0) return error("Failed to send data");
return ERROR_OK;
}
errorret_t serverClientSendString(
serverclient_t * client,
const char_t *data
) {
return serverClientSend(client, (const uint8_t *)data, strlen(data));
}
ssize_t serverClientReceive(
serverclient_t * client,
uint8_t *buffer,
const size_t len
) {
if(client->state != SERVER_CLIENT_STATE_ACCEPTING) return -1;
ssize_t received = recv(client->socket, buffer, len, 0);
if(received < 0) return received;
return received;
}
void serverClientClose(serverclient_t *client) {
client->state = SERVER_CLIENT_STATE_DISCONNECTED;
close(client->socket);
pthread_join(client->thread, NULL);
client->thread = 0;
client->socket = -1;
consolePrint("Client disconnected.");
}
void *serverClientThread(void *arg) {
serverclient_t *client = (serverclient_t *)arg;
char_t buffer[1024];
ssize_t read;
printf("Client thread started for socket %d.\n", client->socket);
// Set socket timeout
setsockopt(
client->socket,
SOL_SOCKET,
SO_RCVTIMEO,
&client->timeout,
sizeof(client->timeout)
);
// Send welcome message
serverClientSendString(client, "DUSK|"DUSK_VERSION);
// Receive version from client
read = serverClientReceive(client, buffer, sizeof(buffer));
// First 5 bytes should be "DUSK|"
if(read < 5 || strncmp(buffer, "DUSK|", 5) != 0) {
serverClientSendString(client, "ERR|Invalid version (0)");
serverClientClose(client);
return NULL;
}
// Next (up to X bytes where X is the length of the version string)
// should be the version string
buffer[read] = '\0'; // Null-terminate the string
printf("Received version: %s\n", buffer);
serverClientClose(client);
return NULL;
}

85
src/server/serverclient.h Normal file
View File

@ -0,0 +1,85 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#define SERVER_CLIENT_TIMEOUT 20000 // 20 seconds
typedef enum {
SERVER_CLIENT_STATE_DISCONNECTED,
SERVER_CLIENT_STATE_ACCEPTING,
} serverclientstate_t;
typedef struct {
int32_t socket;
serverclientstate_t state;
pthread_t thread; // Add thread field
struct timeval timeout; // Add timeout field
} serverclient_t;
/**
* Accepts an incoming connection from a server client.
*/
errorret_t serverClientAccept(
serverclient_t *client,
const int32_t socket
);
/**
* Sends data to a server client.
*
* @param client Pointer to the server client structure.
* @param data Pointer to the data to send.
* @param len Length of the data to send.
* @return Error code indicating success or failure.
*/
errorret_t serverClientSend(
serverclient_t * client,
const uint8_t *data,
const size_t len
);
/**
* Sends a string to a server client.
*
* @param client Pointer to the server client structure.
* @param data Pointer to the string to send.
* @return Error code indicating success or failure.
*/
errorret_t serverClientSendString(
serverclient_t * client,
const char_t *data
);
/**
* Receives data from a server client.
*
* @param client Pointer to the server client structure.
* @param buffer Pointer to the buffer to store received data.
* @param len Max length of the buffer.
* @return Number of bytes received. 0 or less indicates an error.
*/
ssize_t serverClientReceive(
serverclient_t * client,
uint8_t *buffer,
const size_t len
);
/**
* Closes the connection to a server client.
*/
void serverClientClose(
serverclient_t *client
);
/**
* Thread function for handling a server client.
*
* @param arg Pointer to the server client structure.
*/
void *serverClientThread(void *arg);