iuno
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -89,3 +89,6 @@ assets/borrowed
|
||||
*~
|
||||
|
||||
/dist
|
||||
|
||||
/archive0
|
||||
/archive1
|
@ -5,45 +5,10 @@
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
# GLFW
|
||||
# RayLib
|
||||
FetchContent_Declare(
|
||||
glfw
|
||||
GIT_REPOSITORY https://github.com/glfw/glfw
|
||||
GIT_TAG 3.4
|
||||
raylib
|
||||
GIT_REPOSITORY https://github.com/raysan5/raylib
|
||||
GIT_TAG 5.5
|
||||
)
|
||||
FetchContent_MakeAvailable(glfw)
|
||||
|
||||
# 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)
|
||||
FetchContent_MakeAvailable(raylib)
|
@ -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
|
||||
)
|
@ -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
1833
lib/glad/src/glad.c
1833
lib/glad/src/glad.c
File diff suppressed because it is too large
Load Diff
@ -3,5 +3,28 @@
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
add_subdirectory(dusktest)
|
||||
add_subdirectory(dusk)
|
||||
# Libs
|
||||
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)
|
@ -303,15 +303,17 @@ void cmdGet(const consolecmdexec_t *exec) {
|
||||
}
|
||||
|
||||
void cmdSet(const consolecmdexec_t *exec) {
|
||||
assertTrue(
|
||||
exec->argc >= 2,
|
||||
"set command requires 2 arguments."
|
||||
);
|
||||
assertTrue(exec->argc >= 2, "set command requires 2 arguments.");
|
||||
|
||||
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
|
||||
consolevar_t *var = &CONSOLE.variables[i];
|
||||
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -326,3 +328,57 @@ void cmdEcho(const consolecmdexec_t *exec) {
|
||||
|
||||
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
|
||||
);
|
||||
}
|
@ -38,6 +38,10 @@ typedef struct {
|
||||
consolecmd_t *cmdGet;
|
||||
consolecmd_t *cmdSet;
|
||||
pthread_mutex_t lock; // Mutex for thread safety
|
||||
|
||||
// May move these later
|
||||
char_t inputBuffer[CONSOLE_LINE_MAX];
|
||||
int32_t inputIndex;
|
||||
} console_t;
|
||||
|
||||
extern console_t CONSOLE;
|
||||
@ -93,6 +97,16 @@ void consoleExec(const char_t *line);
|
||||
*/
|
||||
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 cmdSet(const consolecmdexec_t *exec);
|
||||
void cmdEcho(const consolecmdexec_t *exec);
|
@ -16,7 +16,12 @@
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <pthread.h>
|
||||
#include <raylib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef bool bool_t;
|
||||
typedef char char_t;
|
||||
|
||||
#define DUSK_NAME "Dusk"
|
||||
#define DUSK_VERSION "1.0.0"
|
@ -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)
|
@ -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();
|
@ -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")
|
@ -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);
|
||||
}
|
||||
}
|
@ -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__)
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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)
|
@ -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;
|
||||
}
|
@ -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();
|
@ -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();
|
||||
}
|
@ -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();
|
@ -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();
|
||||
}
|
@ -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();
|
@ -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)
|
@ -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
|
||||
)
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
@ -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();
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
@ -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
|
||||
)
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
@ -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
|
||||
)
|
@ -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
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
)
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
@ -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();
|
||||
}
|
@ -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);
|
@ -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();
|
||||
}
|
@ -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);
|
@ -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);
|
||||
}
|
@ -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();
|
@ -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;
|
||||
}
|
@ -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
|
||||
);
|
@ -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]);
|
||||
}
|
@ -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();
|
@ -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];
|
@ -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
|
@ -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);
|
||||
}
|
@ -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)
|
@ -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
|
||||
)
|
@ -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();
|
||||
}
|
@ -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();
|
@ -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"
|
@ -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>
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -35,11 +35,21 @@ bool_t errorCheck() {
|
||||
return ERROR_STACK.code != ERROR_OK;
|
||||
}
|
||||
|
||||
errorret_t errorPrint() {
|
||||
if(ERROR_STACK.code == ERROR_OK) return ERROR_OK;
|
||||
const char_t * errorString() {
|
||||
if(!errorCheck()) return NULL;
|
||||
return ERROR_STACK.message;
|
||||
}
|
||||
|
||||
printf("Error: %s\n", ERROR_STACK.message);
|
||||
errorret_t code = ERROR_STACK.code;
|
||||
void errorFlush() {
|
||||
if(!errorCheck()) return;
|
||||
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;
|
||||
}
|
@ -44,6 +44,16 @@ errorret_t errorCode(const errorret_t code, const char_t *message, ...);
|
||||
*/
|
||||
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.
|
||||
*/
|
48
src/main.c
Normal file
48
src/main.c
Normal 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;
|
||||
}
|
@ -6,5 +6,6 @@
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
assertgl.c
|
||||
server.c
|
||||
serverclient.c
|
||||
)
|
192
src/server/server.c
Normal file
192
src/server/server.c
Normal 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
81
src/server/server.h
Normal 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
109
src/server/serverclient.c
Normal 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
85
src/server/serverclient.h
Normal 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);
|
Reference in New Issue
Block a user