Compare commits

...

9 Commits

Author SHA1 Message Date
70056cf4ca Temp only build knulli
Some checks failed
Build Dusk / build-knulli (push) Failing after 21s
2026-03-28 11:02:43 -05:00
5f4ab71ade Add knulli build 2026-03-28 11:02:34 -05:00
f3adb3257b Cleanup knulli 2026-03-28 11:00:18 -05:00
438edda7fd Fixed knulli 2026-03-28 10:56:40 -05:00
d5b0441e6f Fixed GLES support (partially), PSP still not working 2026-03-28 10:51:50 -05:00
9ba0ceb000 Moved texture setting around 2026-03-28 09:48:24 -05:00
9474a68995 Slightly more accurate, likely going to have to change how paletted textures work 2026-03-27 21:01:29 -05:00
09c35f0aa6 Builds on knulli 2026-03-27 20:48:43 -05:00
a2113442cb Builds on knulli 2026-03-27 15:59:26 -05:00
21 changed files with 549 additions and 181 deletions

View File

@@ -9,89 +9,109 @@ on:
- main - main
jobs: jobs:
run-tests: # run-tests:
runs-on: ubuntu-latest # runs-on: ubuntu-latest
steps: # steps:
- name: Checkout repository # - name: Checkout repository
uses: actions/checkout@v6 # uses: actions/checkout@v6
- name: Set up Docker # - name: Set up Docker
uses: docker/setup-docker-action@v5 # uses: docker/setup-docker-action@v5
- name: Run tests in Docker # - name: Run tests in Docker
run: ./scripts/test-linux-docker.sh # run: ./scripts/test-linux-docker.sh
build-linux: # build-linux:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v6
# - name: Set up Docker
# uses: docker/setup-docker-action@v5
# - name: Build Linux
# run: ./scripts/build-linux-docker.sh
# - name: Upload Linux binary
# uses: actions/upload-artifact@v6
# with:
# name: dusk-linux
# path: build-linux/Dusk
# if-no-files-found: error
# build-psp:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v6
# - name: Set up Docker
# uses: docker/setup-docker-action@v5
# - name: Build psp
# run: ./scripts/build-psp-docker.sh
# - name: Move EBOOT.PBP to Dusk subfolder
# run: |
# mkdir -p ./git-artifcats/Dusk/PSP/GAME/Dusk
# cp build-psp/EBOOT.PBP ./git-artifcats/Dusk/PSP/GAME/Dusk/EBOOT.PBP
# - name: Upload psp binary
# uses: actions/upload-artifact@v6
# with:
# name: dusk-psp
# path: ./git-artifcats/Dusk
# if-no-files-found: error
# build-gamecube:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v6
# - name: Set up Docker
# uses: docker/setup-docker-action@v5
# - name: Build GameCube
# run: ./scripts/build-gamecube-docker.sh
# - name: Copy output files.
# run: |
# mkdir -p ./git-artifcats/Dusk
# cp build-gamecube/Dusk.dol ./git-artifcats/Dusk/Dusk.dol
# cp build-gamecube/dusk.dsk ./git-artifcats/Dusk/dusk.dsk
# - name: Upload GameCube binary
# uses: actions/upload-artifact@v6
# with:
# name: dusk-gamecube
# path: ./git-artifcats/Dusk
# build-wii:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v6
# - name: Set up Docker
# uses: docker/setup-docker-action@v5
# - name: Build Wii
# run: ./scripts/build-wii-docker.sh
# - name: Copy output files.
# run: |
# mkdir -p ./git-artifcats/Dusk/apps/Dusk
# cp build-wii/Dusk.dol ./git-artifcats/Dusk/apps/Dusk/Dusk.dol
# cp build-wii/dusk.dsk ./git-artifcats/Dusk/apps/Dusk/dusk.dsk
# cp docker/dolphin/meta.xml ./git-artifcats/Dusk/apps/Dusk/meta.xml
# - name: Upload Wii binary
# uses: actions/upload-artifact@v6
# with:
# name: dusk-wii
# path: ./git-artifcats/Dusk
build-knulli:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Set up Docker - name: Set up Docker
uses: docker/setup-docker-action@v5 uses: docker/setup-docker-action@v5
- name: Build Linux - name: Build knulli
run: ./scripts/build-linux-docker.sh run: ./scripts/build-knulli-docker.sh
- name: Upload Linux binary - name: Upload knulli binary
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: - name: Move output to Dusk subfolder
name: dusk-linux
path: build-linux/Dusk
if-no-files-found: error
build-psp:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build psp
run: ./scripts/build-psp-docker.sh
- name: Move EBOOT.PBP to Dusk subfolder
run: |
mkdir -p ./git-artifcats/Dusk/PSP/GAME/Dusk
cp build-psp/EBOOT.PBP ./git-artifcats/Dusk/PSP/GAME/Dusk/EBOOT.PBP
- name: Upload psp binary
uses: actions/upload-artifact@v6
with:
name: dusk-psp
path: ./git-artifcats/Dusk
if-no-files-found: error
build-gamecube:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build GameCube
run: ./scripts/build-gamecube-docker.sh
- name: Copy output files.
run: | run: |
mkdir -p ./git-artifcats/Dusk mkdir -p ./git-artifcats/Dusk
cp build-gamecube/Dusk.dol ./git-artifcats/Dusk/Dusk.dol cp -r build-knulli/dusk ./git-artifcats/Dusk
cp build-gamecube/dusk.dsk ./git-artifcats/Dusk/dusk.dsk
- name: Upload GameCube binary
uses: actions/upload-artifact@v6
with: with:
name: dusk-gamecube name: dusk-knulli
path: ./git-artifcats/Dusk path: ./git-artifcats/Dusk
if-no-files-found: error
build-wii:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build Wii
run: ./scripts/build-wii-docker.sh
- name: Copy output files.
run: |
mkdir -p ./git-artifcats/Dusk/apps/Dusk
cp build-wii/Dusk.dol ./git-artifcats/Dusk/apps/Dusk/Dusk.dol
cp build-wii/dusk.dsk ./git-artifcats/Dusk/apps/Dusk/dusk.dsk
cp docker/dolphin/meta.xml ./git-artifcats/Dusk/apps/Dusk/meta.xml
- name: Upload Wii binary
uses: actions/upload-artifact@v6
with:
name: dusk-wii
path: ./git-artifcats/Dusk

View File

@@ -0,0 +1,45 @@
# Find link platform-specific libraries
set(OpenGL_GL_PREFERENCE LEGACY)
find_package(SDL2 REQUIRED)
find_library(EGL_LIB EGL REQUIRED)
find_library(GL_LIB GL REQUIRED)
find_package(OpenGL REQUIRED)
# Setup endianess at compile time to optimize.
include(TestBigEndian)
test_big_endian(IS_BIG_ENDIAN)
if(IS_BIG_ENDIAN)
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_PLATFORM_ENDIAN_BIG
)
else()
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_PLATFORM_ENDIAN_LITTLE
)
endif()
# Link required libraries.
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
SDL2
pthread
OpenGL::GLES2
${GL_LIB}
${EGL_LIB}
m
)
# Define platform-specific macros.
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_SDL2
DUSK_OPENGL
DUSK_OPENGL_ES
DUSK_LINUX
DUSK_DISPLAY_SIZE_DYNAMIC
DUSK_DISPLAY_WIDTH_DEFAULT=640
DUSK_DISPLAY_HEIGHT_DEFAULT=480
DUSK_DISPLAY_SCREEN_HEIGHT=240
DUSK_INPUT_KEYBOARD
DUSK_INPUT_POINTER
DUSK_INPUT_GAMEPAD
DUSK_TIME_DYNAMIC
)

View File

@@ -28,6 +28,7 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_SDL2 DUSK_SDL2
DUSK_OPENGL DUSK_OPENGL
# DUSK_OPENGL_LEGACY
DUSK_LINUX DUSK_LINUX
DUSK_DISPLAY_SIZE_DYNAMIC DUSK_DISPLAY_SIZE_DYNAMIC
DUSK_DISPLAY_WIDTH_DEFAULT=640 DUSK_DISPLAY_WIDTH_DEFAULT=640

View File

@@ -0,0 +1,29 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_ASM_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_SYSROOT /)
set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu)
set(CMAKE_CXX_COMPILER_TARGET aarch64-linux-gnu)
set(CMAKE_FIND_ROOT_PATH
/usr/aarch64-linux-gnu
/usr/lib/aarch64-linux-gnu
/usr/include/aarch64-linux-gnu
)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(ENV{PKG_CONFIG_LIBDIR} "/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig")
set(ENV{PKG_CONFIG_PATH} "/usr/lib/aarch64-linux-gnu/pkgconfig")
set(CMAKE_PREFIX_PATH "/usr/aarch64-linux-gnu;/usr/lib/aarch64-linux-gnu")
# Optional: helps some Find modules
set(SDL2_DIR "/usr/lib/aarch64-linux-gnu/cmake/SDL2" CACHE PATH "")

35
docker/knulli/Dockerfile Normal file
View File

@@ -0,0 +1,35 @@
FROM debian:trixie
ENV DEBIAN_FRONTEND=noninteractive
WORKDIR /workdir
RUN dpkg --add-architecture arm64 && \
apt-get update && \
apt-get install -y --no-install-recommends \
crossbuild-essential-arm64 \
ca-certificates \
pkg-config \
cmake \
make \
ninja-build \
git \
file \
python3 \
python3-pip \
python3-polib \
python3-pil \
python3-dotenv \
python3-pyqt5 \
python3-opengl \
liblua5.4-dev:arm64 \
xz-utils:arm64 \
libbz2-dev:arm64 \
zlib1g-dev:arm64 \
libzip-dev:arm64 \
libssl-dev:arm64 \
libsdl2-dev:arm64 \
liblzma-dev:arm64 \
libopengl0:arm64 \
libgl1:arm64 \
libegl1:arm64 \
libgles2:arm64 \
libgl1-mesa-dev:arm64 && \
rm -rf /var/lib/apt/lists/*

3
scripts/build-knulli-docker.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
docker build -t dusk-knulli -f docker/knulli/Dockerfile .
docker run --rm -v $(pwd):/workdir dusk-knulli /bin/bash -c "./scripts/build-knulli.sh"

24
scripts/build-knulli.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
cmake -S . -B build-knulli -G Ninja \
-DDUSK_BUILD_TESTS=ON \
-DDUSK_TARGET_SYSTEM=knulli \
-DCMAKE_TOOLCHAIN_FILE=./cmake/toolchains/aarch64-linux-gnu.cmake \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-knulli -- -j$(nproc)
# Copy necessary libs out
mkdir -p ./build-knulli/dusk
cp ./build-knulli/Dusk ./build-knulli/dusk/Dusk
cp ./build-knulli/dusk.dsk ./build-knulli/dusk/dusk.dsk
echo '#!/bin/bash' > build-knulli/dusk/Dusk.sh
echo 'cd "$(dirname "$(readlink -f "$0")")"' >> build-knulli/dusk/Dusk.sh
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(dirname "$(readlink -f "$0")")' >> build-knulli/dusk/Dusk.sh
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/gl4es' >> build-knulli/dusk/Dusk.sh
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib' >> build-knulli/dusk/Dusk.sh
echo '$(dirname "$(readlink -f "$0")")/Dusk' >> build-knulli/dusk/Dusk.sh
chmod +x build-knulli/dusk/Dusk.sh
cp /usr/lib/aarch64-linux-gnu/liblua5.4.so.0 build-knulli/dusk/
# cp /usr/lib/aarch64-linux-gnu/libSDL2-2.0.so.0 build-knulli/dusk/
# cp /usr/lib/aarch64-linux-gnu/libGL.so.1 build-knulli/dusk/
# cp /usr/lib/aarch64-linux-gnu/libEGL.so.1 build-knulli/dusk/
# cp /usr/lib/aarch64-linux-gnu/libGLESv2.so.2 build-knulli/dusk/

View File

@@ -5,7 +5,7 @@
add_subdirectory(dusk) add_subdirectory(dusk)
if(DUSK_TARGET_SYSTEM STREQUAL "linux") if(DUSK_TARGET_SYSTEM STREQUAL "linux" OR DUSK_TARGET_SYSTEM STREQUAL "knulli")
add_subdirectory(dusklinux) add_subdirectory(dusklinux)
add_subdirectory(dusksdl2) add_subdirectory(dusksdl2)
add_subdirectory(duskgl) add_subdirectory(duskgl)

View File

@@ -7,6 +7,7 @@
#include "assert.h" #include "assert.h"
#include "log/log.h" #include "log/log.h"
#include "util/string.h"
#ifndef DUSK_ASSERTIONS_FAKED #ifndef DUSK_ASSERTIONS_FAKED
#ifdef DUSK_TEST_ASSERT #ifdef DUSK_TEST_ASSERT
@@ -98,4 +99,14 @@
) { ) {
assertUnreachableImpl(file, line, message); assertUnreachableImpl(file, line, message);
} }
void assertStringEqualImpl(
const char *file,
const int32_t line,
const char *a,
const char *b,
const char *message
) {
assertTrueImpl(file, line, stringCompare(a, b) == 0, message);
}
#endif #endif

View File

@@ -104,6 +104,23 @@
const char *message const char *message
); );
/**
* Asserts two strings to be equal.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param a First string to compare.
* @param b Second string to compare.
* @param message Message to throw against assertion failure.
*/
void assertStringEqualImpl(
const char *file,
const int32_t line,
const char *a,
const char *b,
const char *message
);
/** /**
* Asserts a given value to be true. * Asserts a given value to be true.
* *
@@ -178,6 +195,16 @@
#define assertStrLenMin(str, len, message) \ #define assertStrLenMin(str, len, message) \
assertTrue(strlen(str) >= len, message) assertTrue(strlen(str) >= len, message)
/**
* Asserts two strings to be equal.
*
* @param a First string to compare.
* @param b Second string to compare.
* @param message Message to throw against assertion failure.
*/
#define assertStringEqual(a, b, message) \
assertStringEqualImpl(__FILE__, __LINE__, a, b, message)
#else #else
// If assertions are faked, we define the macros to do nothing. // If assertions are faked, we define the macros to do nothing.
#define assertTrue(x, message) ((void)0) #define assertTrue(x, message) ((void)0)

View File

@@ -22,12 +22,9 @@
display_t DISPLAY = { 0 }; display_t DISPLAY = { 0 };
texture_t TEXTURE; texture_t PALETTE_TEXTURE;
texture_t UNCOMPRESSED_TEXTURE;
uint8_t indices[2 * 2] = { texture_t COMPRESSED_TEXTURE;
0, 1,
2, 3
};
errorret_t displayInit(void) { errorret_t displayInit(void) {
memoryZero(&DISPLAY, sizeof(DISPLAY)); memoryZero(&DISPLAY, sizeof(DISPLAY));
@@ -47,11 +44,26 @@ errorret_t displayInit(void) {
PALETTES[0].colors[1] = COLOR_GREEN; PALETTES[0].colors[1] = COLOR_GREEN;
PALETTES[0].colors[2] = COLOR_BLUE; PALETTES[0].colors[2] = COLOR_BLUE;
PALETTES[0].colors[3] = COLOR_WHITE; PALETTES[0].colors[3] = COLOR_WHITE;
PALETTES[0].count = 4; PALETTES[0].colors[4] = COLOR_MAGENTA;
PALETTES[0].colors[5] = COLOR_CYAN;
PALETTES[0].colors[6] = COLOR_YELLOW;
PALETTES[0].colors[7] = COLOR_BLACK;
PALETTES[0].count = 8;
uint8_t indices[64] = {
0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,
5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7
};
errorChain(textureInit( errorChain(textureInit(
&TEXTURE, &PALETTE_TEXTURE,
2, 2, 8, 8,
TEXTURE_FORMAT_PALETTE, TEXTURE_FORMAT_PALETTE,
(texturedata_t){ (texturedata_t){
.paletted = { .paletted = {
@@ -61,6 +73,24 @@ errorret_t displayInit(void) {
} }
)); ));
errorChain(textureInit(
&UNCOMPRESSED_TEXTURE,
8, 8,
TEXTURE_FORMAT_RGBA,
(texturedata_t){
.rgbaColors = (color_t[]){
COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_WHITE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW, COLOR_BLACK,
COLOR_GREEN, COLOR_BLUE, COLOR_WHITE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW, COLOR_BLACK, COLOR_RED,
COLOR_BLUE, COLOR_WHITE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW, COLOR_BLACK, COLOR_RED, COLOR_GREEN,
COLOR_WHITE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE,
COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_WHITE,
COLOR_CYAN, COLOR_YELLOW, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_WHITE, COLOR_MAGENTA,
COLOR_YELLOW, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_WHITE, COLOR_MAGENTA, COLOR_CYAN,
COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_WHITE, COLOR_MAGENTA, COLOR_CYAN, COLOR_YELLOW
}
}
));
errorOk(); errorOk();
} }
@@ -100,7 +130,8 @@ errorret_t displayUpdate(void) {
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model)); errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &TEXTURE)); // errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &PALETTE_TEXTURE));
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, &UNCOMPRESSED_TEXTURE));
errorChain(spriteBatchPush( errorChain(spriteBatchPush(
0.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,

View File

@@ -12,8 +12,8 @@
#define PALETTE_COUNT 6 #define PALETTE_COUNT 6
typedef struct { typedef struct {
uint8_t count;
color_t colors[PALETTE_COLOR_COUNT]; color_t colors[PALETTE_COLOR_COUNT];
uint8_t count;
} palette_t; } palette_t;
extern palette_t PALETTES[PALETTE_COUNT]; extern palette_t PALETTES[PALETTE_COUNT];

View File

@@ -26,6 +26,7 @@ typedef union texturedata_u {
palette_t *palette; palette_t *palette;
} paletted; } paletted;
color_t *rgbaColors; color_t *rgbaColors;
uint8_t *compressedData;
} texturedata_t; } texturedata_t;
/** /**

View File

@@ -29,8 +29,6 @@ errorret_t displayOpenGLInit(void) {
errorChain(errorGLCheck()); errorChain(errorGLCheck());
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
errorChain(errorGLCheck()); errorChain(errorGLCheck());
glPixelStorei(GL_PACK_ALIGNMENT, 1);
errorChain(errorGLCheck());
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
errorChain(errorGLCheck()); errorChain(errorGLCheck());

View File

@@ -28,6 +28,11 @@ errorret_t shaderInitGL(shadergl_t *shader, const shaderdefinitiongl_t *def) {
SHADER_LEGACY.boundShader = NULL; SHADER_LEGACY.boundShader = NULL;
errorOk(); errorOk();
#else #else
assertNotNull(def->vert, "Vertex shader source cannot be null");
assertNotNull(def->frag, "Fragment shader source cannot be null");
shader->setTexture = def->setTexture;
// Create vertex shader // Create vertex shader
shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER); shader->vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
errorret_t err = errorGLCheck(); errorret_t err = errorGLCheck();
@@ -212,6 +217,12 @@ errorret_t shaderSetTextureGL(
assertStrLenMin(name, 1, "Uniform name cannot be empty"); assertStrLenMin(name, 1, "Uniform name cannot be empty");
#ifdef DUSK_OPENGL_LEGACY #ifdef DUSK_OPENGL_LEGACY
assertStringEqual(
name,
SHADER_UNLIT_TEXTURE,
"Only one texture supported in legacy opengl."
);
if(texture == NULL) { if(texture == NULL) {
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
errorChain(errorGLCheck()); errorChain(errorGLCheck());
@@ -222,48 +233,12 @@ errorret_t shaderSetTextureGL(
errorChain(errorGLCheck()); errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, texture->id); glBindTexture(GL_TEXTURE_2D, texture->id);
errorChain(errorGLCheck()); errorChain(errorGLCheck());
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
errorChain(errorGLCheck());
#else #else
if(texture == NULL) { if(shader->setTexture == NULL) {
glActiveTexture(GL_TEXTURE0); assertUnreachable("Shader does not support setting textures.");
errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, 0);
errorChain(errorGLCheck());
errorOk();
} }
errorChain(shader->setTexture(shader, name, texture));
GLint location;
errorChain(shaderParamGetLocationGL(shader, name, &location));
glActiveTexture(GL_TEXTURE0);
errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, texture->id);
errorChain(errorGLCheck());
glUniform1i(location, 0);
errorChain(errorGLCheck());
if(texture->format == TEXTURE_FORMAT_PALETTE) {
shaderParamGetLocationGL(shader, "u_ColorCount", &location);
glUniform1i(location, texture->palette->count);
errorChain(errorGLCheck());
shaderParamGetLocationGL(shader, "u_Colors", &location);
GLuint paletteData[texture->palette->count];
for(size_t i = 0; i < texture->palette->count; i++) {
color_t color = texture->palette->colors[i];
paletteData[i] = (
((uint32_t)color.r << 24) |
((uint32_t)color.g << 16) |
((uint32_t)color.b << 8) |
((uint32_t)color.a << 0)
);
}
glUniform1uiv(location, texture->palette->count, paletteData);
errorChain(errorGLCheck());
}
#endif #endif
errorOk(); errorOk();

View File

@@ -9,28 +9,35 @@
#include "error/errorgl.h" #include "error/errorgl.h"
#include "display/texture/texture.h" #include "display/texture/texture.h"
typedef struct { typedef struct shadergl_s shadergl_t;
typedef errorret_t (*shadersettexturefn_t)(
shadergl_t *,
const char_t *,
texture_t *
);
typedef struct shadergl_s {
#ifdef DUSK_OPENGL_LEGACY #ifdef DUSK_OPENGL_LEGACY
void *nothing; mat4 view;
mat4 proj;
mat4 model;
#else #else
GLuint shaderProgramId; GLuint shaderProgramId;
GLuint vertexShaderId; GLuint vertexShaderId;
GLuint fragmentShaderId; GLuint fragmentShaderId;
#endif shadersettexturefn_t setTexture;
#if DUSK_OPENGL_LEGACY
mat4 view;
mat4 proj;
mat4 model;
#endif #endif
} shadergl_t; } shadergl_t;
typedef struct { typedef struct {
#ifdef DUSK_OPENGL_LEGACY #ifdef DUSK_OPENGL_LEGACY
void *nothing; void *nothing;
#else #else
const char_t *vert; const char_t *vert;
const char_t *frag; const char_t *frag;
shadersettexturefn_t setTexture;
#endif #endif
} shaderdefinitiongl_t; } shaderdefinitiongl_t;

View File

@@ -6,46 +6,184 @@
*/ */
#include "display/shader/shaderunlit.h" #include "display/shader/shaderunlit.h"
#include "assert/assertgl.h"
#ifdef DUSK_OPENGL_LEGACY #ifdef DUSK_OPENGL_LEGACY
shaderdefinition_t SHADER_UNLIT_DEFINITION = { 0 }; shaderdefinition_t SHADER_UNLIT_DEFINITION = { 0 };
#else #else
errorret_t shaderUnlitSetTextureGL(
shadergl_t *shader,
const char_t *name,
texture_t *texture
) {
assertNotNull(shader, "Shader cannot be null");
assertStrLenMin(name, 1, "Uniform name cannot be empty");
assertStringEqual(
name,
SHADER_UNLIT_TEXTURE,
"Only one texture supported in unlit shader."
);
GLint locTexture, locType, locColorCount, locColors;
errorChain(shaderParamGetLocationGL(shader, "u_TextureType", &locType));
// NULL textures
if(texture == NULL) {
glUniform1i(locType, 0);
errorChain(errorGLCheck());
errorOk();
}
// Set texture.
glActiveTexture(GL_TEXTURE0);
errorChain(errorGLCheck());
glBindTexture(GL_TEXTURE_2D, texture->id);
errorChain(errorGLCheck());
errorChain(shaderParamGetLocationGL(shader, name, &locTexture));
glUniform1i(locTexture, 0);
errorChain(errorGLCheck());
// Set texture type
if(texture->format == TEXTURE_FORMAT_PALETTE) {
glUniform1i(locType, 2);
errorChain(errorGLCheck());
shaderParamGetLocationGL(shader, "u_ColorCount", &locColorCount);
glUniform1i(locColorCount, texture->palette->count);
errorChain(errorGLCheck());
shaderParamGetLocationGL(shader, "u_Colors", &locColors);
GLuint paletteData[texture->palette->count];
for(size_t i = 0; i < texture->palette->count; i++) {
color_t color = texture->palette->colors[i];
paletteData[i] = (
((uint32_t)color.r << 24) |
((uint32_t)color.g << 16) |
((uint32_t)color.b << 8) |
((uint32_t)color.a << 0)
);
}
glUniform1uiv(locColors, texture->palette->count, paletteData);
errorChain(errorGLCheck());
} else {
glUniform1i(locType, 1);
errorChain(errorGLCheck());
}
errorOk();
}
shaderdefinition_t SHADER_UNLIT_DEFINITION = { shaderdefinition_t SHADER_UNLIT_DEFINITION = {
.vert = .vert =
"#version 330 core\n" #ifdef DUSK_OPENGL_ES
"layout(location = 0) in vec3 a_Pos;\n" "#version 300 es\n"
"layout(location = 1) in vec2 a_TexCoord;\n" "precision mediump float;\n"
"layout(location = 2) in vec4 a_Color;\n" // Attributes
"uniform mat4 u_Proj;\n" "layout(location = 0) in vec3 a_Pos;\n"
"uniform mat4 u_View;\n" "layout(location = 1) in vec2 a_TexCoord;\n"
"uniform mat4 u_Model;\n" "layout(location = 2) in vec4 a_Color;\n"
"out vec4 v_Color;\n" // Uniforms
"out vec2 v_TexCoord;\n" "uniform mat4 u_Proj;\n"
"void main() {\n" "uniform mat4 u_View;\n"
" gl_Position = u_Proj * u_View * u_Model * vec4(a_Pos, 1.0);\n" "uniform mat4 u_Model;\n"
" v_Color = a_Color;\n" // Vertex shader outputs
" v_TexCoord = a_TexCoord;\n" "out vec4 v_Color;\n"
"}\n", "out vec2 v_TexCoord;\n"
"void main() {\n"
" gl_Position = u_Proj * u_View * u_Model * vec4(a_Pos, 1.0);\n"
" v_Color = a_Color;\n"
" v_TexCoord = a_TexCoord;\n"
"}\n",
#else
"#version 330 core\n"
// Attributes
"layout(location = 0) in vec3 a_Pos;\n"
"layout(location = 1) in vec2 a_TexCoord;\n"
"layout(location = 2) in vec4 a_Color;\n"
// Uniforms
"uniform mat4 u_Proj;\n"
"uniform mat4 u_View;\n"
"uniform mat4 u_Model;\n"
// Vertex shader outputs
"out vec4 v_Color;\n"
"out vec2 v_TexCoord;\n"
"void main() {\n"
" gl_Position = u_Proj * u_View * u_Model * vec4(a_Pos, 1.0);\n"
" v_Color = a_Color;\n"
" v_TexCoord = a_TexCoord;\n"
"}\n",
#endif
.frag = .frag =
"#version 330 core\n" #ifdef DUSK_OPENGL_ES
"uniform sampler2D u_Texture;\n" "#version 300 es\n"
"uniform uint u_Colors[256];\n" "precision mediump float;\n"
"uniform int u_ColorCount;\n" // Uniforms
"in vec4 v_Color;\n" "uniform sampler2D u_Texture;\n"
"in vec2 v_TexCoord;\n" "uniform int u_TextureType;\n"
"out vec4 FragColor;\n" "uniform vec4 u_Colors[256];\n"// For paletted textures.
"void main() {\n" "uniform int u_ColorCount;\n"
" vec4 texColor = texture(u_Texture, v_TexCoord);\n" // Fragment shader inputs
" uint index = uint(floor(texColor.r * 255.0));\n" "in vec4 v_Color;\n"
" uint palColor = u_Colors[index];\n" "in vec2 v_TexCoord;\n"
" float r = float((palColor >> 24) & 0xFFu) / 255.0;\n" // Fragment shader output
" float g = float((palColor >> 16) & 0xFFu) / 255.0;\n" "out vec4 FragColor;\n"
" float b = float((palColor >> 8) & 0xFFu) / 255.0;\n" "void main() {\n"
" float a = float((palColor >> 0) & 0xFFu) / 255.0;\n" " if(u_TextureType == 0) {\n"// No texture
" vec4 paletteColor = vec4(r, g, b, a);\n" " FragColor = v_Color;\n"
" FragColor = paletteColor;\n" " return;\n"
// " FragColor = paletteColor * v_Color;\n" " }\n"
"}\n" " if(u_TextureType == 1) {\n"// Regular texture
" FragColor = texture(u_Texture, v_TexCoord) * v_Color;\n"
" return;\n"
" }\n"
" if(u_TextureType == 2) {\n"// Paletted texture
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
" int index = int(floor(texColor.r * 255.0));\n"
" vec4 paletteColor = u_Colors[index];\n"
" FragColor = paletteColor;\n"
" return;\n"
" }\n"
" FragColor = v_Color;\n"// Unknown texture type?
"}\n",
#else
"#version 330 core\n"
// Uniforms
"uniform sampler2D u_Texture;\n"
"uniform int u_TextureType;\n"
"uniform uint u_Colors[256];\n"// For paletted textures.
"uniform int u_ColorCount;\n"
// Fragment shader inputs
"in vec4 v_Color;\n"
"in vec2 v_TexCoord;\n"
// Fragment shader output
"out vec4 FragColor;\n"
"void main() {\n"
" if(u_TextureType == 0) {\n"// No texture
" FragColor = v_Color;\n"
" return;\n"
" }\n"
" if(u_TextureType == 1) {\n"// Regular texture
" FragColor = texture(u_Texture, v_TexCoord) * v_Color;\n"
" return;\n"
" }\n"
" if(u_TextureType == 2) {\n"// Paletted texture
" vec4 texColor = texture(u_Texture, v_TexCoord);\n"
" uint index = uint(floor(texColor.r * 255.0));\n"
" uint palColor = u_Colors[index];\n"
" float r = float((palColor >> 24) & 0xFFu) / 255.0;\n"
" float g = float((palColor >> 16) & 0xFFu) / 255.0;\n"
" float b = float((palColor >> 8) & 0xFFu) / 255.0;\n"
" float a = float((palColor >> 0) & 0xFFu) / 255.0;\n"
" vec4 paletteColor = vec4(r, g, b, a);\n"
" FragColor = paletteColor;\n"
" return;\n"
" }\n"
" FragColor = v_Color;\n"// Unknown texture type?
"}\n",
#endif
.setTexture = shaderUnlitSetTextureGL
}; };
#endif #endif

View File

@@ -25,16 +25,25 @@ errorret_t textureInitGL(
switch(format) { switch(format) {
case TEXTURE_FORMAT_RGBA: case TEXTURE_FORMAT_RGBA:
glTexImage2D( glTexImage2D(
GL_TEXTURE_2D, 0, format, width, height, 0, GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
format, GL_UNSIGNED_BYTE, (void*)data.rgbaColors GL_RGBA, GL_UNSIGNED_BYTE, (void*)data.rgbaColors
); );
errorChain(errorGLCheck()); errorChain(errorGLCheck());
break; break;
case TEXTURE_FORMAT_PALETTE: case TEXTURE_FORMAT_PALETTE:
texture->palette = data.paletted.palette; texture->palette = data.paletted.palette;
assertTrue(
texture->palette == &PALETTES[0],
"Only the first palette is supported in legacy opengl."
);
#ifdef DUSK_OPENGL_LEGACY #ifdef DUSK_OPENGL_LEGACY
glColorTableEXT(
GL_TEXTURE_2D, GL_RGBA, texture->palette->count, GL_RGBA,
GL_UNSIGNED_BYTE, (const void*)texture->palette->colors
);
errorChain(errorGLCheck());
glTexImage2D( glTexImage2D(
GL_TEXTURE_2D, GL_TEXTURE_2D,
0, GL_COLOR_INDEX8_EXT, 0, GL_COLOR_INDEX8_EXT,
@@ -42,13 +51,7 @@ errorret_t textureInitGL(
0, GL_COLOR_INDEX8_EXT, 0, GL_COLOR_INDEX8_EXT,
GL_UNSIGNED_BYTE, (void*)data.paletted.indices GL_UNSIGNED_BYTE, (void*)data.paletted.indices
); );
glColorTableEXT(
GL_TEXTURE_2D, GL_RGBA, texture->palette->count, GL_RGBA,
GL_UNSIGNED_BYTE, (const void*)texture->palette->colors
);
errorChain(errorGLCheck()); errorChain(errorGLCheck());
#else #else
// For modern systems we send to only the R channel and the shader does // For modern systems we send to only the R channel and the shader does
// the rest. // the rest.
@@ -74,6 +77,10 @@ errorret_t textureInitGL(
errorChain(errorGLCheck()); errorChain(errorGLCheck());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
errorChain(errorGLCheck()); errorChain(errorGLCheck());
#ifdef DUSK_OPENGL_LEGACY
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
#endif
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
errorChain(errorGLCheck()); errorChain(errorGLCheck());

View File

@@ -13,6 +13,7 @@ typedef union texturedata_u texturedata_t;
typedef enum { typedef enum {
TEXTURE_FORMAT_RGBA = GL_RGBA, TEXTURE_FORMAT_RGBA = GL_RGBA,
TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT, TEXTURE_FORMAT_PALETTE = GL_COLOR_INDEX8_EXT,
// TEXTURE_FORMAT_DXT5 = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
} textureformatgl_t; } textureformatgl_t;
typedef struct { typedef struct {

View File

@@ -6,6 +6,20 @@
*/ */
#pragma once #pragma once
#define GL_GLEXT_PROTOTYPES #ifdef DUSK_OPENGL_ES
#include <GL/gl.h> #include <GLES3/gl3.h>
#include <GL/glext.h> #define GL_COLOR_INDEX8_EXT 0x80E5
#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER
#define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE
#define glCheckFramebufferStatusEXT glCheckFramebufferStatus
#define glDeleteFramebuffersEXT glDeleteFramebuffers
#define glGenFramebuffersEXT glGenFramebuffers
#define glBindFramebufferEXT glBindFramebuffer
#define glFramebufferTexture2DEXT glFramebufferTexture2D
#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0
#define glClearDepth(depth) glClearDepthf(depth)
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#endif

View File

@@ -19,4 +19,5 @@ void logError(const char_t *message, ...) {
va_start(args, message); va_start(args, message);
vprintf(message, args); vprintf(message, args);
va_end(args); va_end(args);
fflush(stdout);
} }