Added screen

This commit is contained in:
2025-09-12 15:56:21 -05:00
parent 9b98181d28
commit 067b0d2e9f
10 changed files with 264 additions and 110 deletions

View File

@@ -9,6 +9,7 @@ target_sources(${DUSK_TARGET_NAME}
display.c
camera.c
tileset.c
screen.c
)
# Subdirectories

View File

@@ -12,6 +12,7 @@
#include "display/spritebatch/spritebatch.h"
#include "display/ui/ui.h"
#include "display/mesh/quad.h"
#include "display/screen.h"
display_t DISPLAY;
@@ -68,6 +69,7 @@ errorret_t displayInit(void) {
spriteBatchInit();
errorChain(uiInit());
errorChain(sceneManagerInit());
screenInit();
errorOk();
}
@@ -85,24 +87,10 @@ errorret_t displayUpdate(void) {
break;
}
}
// TODO: move to framebuffer component
int32_t windowWidth, windowHeight;
#if PSP
windowWidth = DISPLAY_WINDOW_WIDTH_DEFAULT;
windowHeight = DISPLAY_WINDOW_HEIGHT_DEFAULT;
#else
SDL_GetWindowSize(DISPLAY.window, &windowWidth, &windowHeight);
#endif
glViewport(0, 0, windowWidth, windowHeight);
#endif
spriteBatchClear();
frameBufferBind(&FRAMEBUFFER_BACKBUFFER);
frameBufferClear(
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
COLOR_CORNFLOWER_BLUE
);
screenBind();
sceneManagerUpdate();
uiUpdate();
@@ -110,6 +98,8 @@ errorret_t displayUpdate(void) {
sceneManagerRender();
uiRender();
screenUnbindAndRender();
#if DISPLAY_SDL2
SDL_GL_SwapWindow(DISPLAY.window);
#endif
@@ -124,6 +114,7 @@ errorret_t displayUpdate(void) {
}
errorret_t displayDispose(void) {
screenDispose();
sceneManagerDispose();
uiDispose();
spriteBatchDispose();

View File

@@ -6,37 +6,10 @@
*/
#pragma once
#include "displaydefs.h"
#include "error/error.h"
#if DISPLAY_SDL2
#include <SDL2/SDL.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#ifndef DISPLAY_SIZE_DYNAMIC
#define DISPLAY_SIZE_DYNAMIC 1
#endif
#else
#error "Need to specify display backend."
#endif
#ifndef DISPLAY_WIDTH
#define DISPLAY_WIDTH 320
#endif
#ifndef DISPLAY_HEIGHT
#define DISPLAY_HEIGHT 240
#endif
#ifndef DISPLAY_WINDOW_WIDTH_DEFAULT
#define DISPLAY_WINDOW_WIDTH_DEFAULT DISPLAY_WIDTH
#endif
#ifndef DISPLAY_WINDOW_HEIGHT_DEFAULT
#define DISPLAY_WINDOW_HEIGHT_DEFAULT DISPLAY_HEIGHT
#endif
#include "display/camera.h"
#include "display/framebuffer/framebuffer.h"
typedef struct {
#if DISPLAY_SDL2

36
src/display/displaydefs.h Normal file
View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#if DISPLAY_SDL2
#include <SDL2/SDL.h>
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#ifndef DISPLAY_SIZE_DYNAMIC
#define DISPLAY_SIZE_DYNAMIC 1
#endif
#else
#error "Need to specify display backend."
#endif
#ifndef DISPLAY_WIDTH
#define DISPLAY_WIDTH 320
#endif
#ifndef DISPLAY_HEIGHT
#define DISPLAY_HEIGHT 240
#endif
#ifndef DISPLAY_WINDOW_WIDTH_DEFAULT
#define DISPLAY_WINDOW_WIDTH_DEFAULT DISPLAY_WIDTH
#endif
#ifndef DISPLAY_WINDOW_HEIGHT_DEFAULT
#define DISPLAY_WINDOW_HEIGHT_DEFAULT DISPLAY_HEIGHT
#endif

View File

@@ -6,6 +6,7 @@
*/
#include "framebuffer.h"
#include "display/display.h"
#include "assert/assert.h"
#include "util/memory.h"
@@ -19,6 +20,41 @@ void frameBufferInitBackbuffer() {
FRAMEBUFFER_BOUND = &FRAMEBUFFER_BACKBUFFER;
}
#if DISPLAY_SIZE_DYNAMIC == 1
void frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
) {
#if DISPLAY_SDL2 == 1
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
assertTrue(width > 0 && height > 0, "Width & height must be greater than 0");
memoryZero(framebuffer, sizeof(framebuffer_t));
textureInit(&framebuffer->texture, width, height, GL_RGBA, (texturedata_t){
.rgba = { .colors = NULL }
});
glGenFramebuffersEXT(1, &framebuffer->id);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
glFramebufferTexture2DEXT(
GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, framebuffer->texture.id, 0
);
if(
glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) !=
GL_FRAMEBUFFER_COMPLETE_EXT
) {
assertUnreachable("Framebuffer is not complete");
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
#endif
}
#endif
int32_t frameBufferGetWidth(const framebuffer_t *framebuffer) {
#if DISPLAY_SDL2
if(framebuffer == &FRAMEBUFFER_BACKBUFFER) {
@@ -31,8 +67,7 @@ int32_t frameBufferGetWidth(const framebuffer_t *framebuffer) {
#endif
}
assertUnreachable("Framebuffer width not implemented");
return 0;
return framebuffer->texture.width;
#endif
}
@@ -48,8 +83,7 @@ int32_t frameBufferGetHeight(const framebuffer_t *framebuffer) {
#endif
}
assertUnreachable("Framebuffer height not implemented");
return 0;
return framebuffer->texture.height;
#endif
}
@@ -78,6 +112,7 @@ void frameBufferBind(const framebuffer_t *framebuffer) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
#endif
}
glViewport(
0, 0,
frameBufferGetWidth(framebuffer), frameBufferGetHeight(framebuffer)
@@ -109,11 +144,6 @@ void frameBufferClear(uint8_t flags, color_t color) {
#endif
}
/**
* Disposes of the framebuffer using EXT methods.
*
* @param framebuffer The framebuffer to dispose of.
*/
void frameBufferDispose(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
@@ -125,56 +155,8 @@ void frameBufferDispose(framebuffer_t *framebuffer) {
#if DISPLAY_SIZE_DYNAMIC == 0
assertUnreachable("Dynamic size framebuffers not supported");
#else
textureDispose(&framebuffer->texture);
glDeleteFramebuffersEXT(1, &framebuffer->id);
#endif
#endif
}
// #if RENDER_USE_FRAMEBUFFER
// void frameBufferInit(
// framebuffer_t *framebuffer,
// const uint32_t width,
// const uint32_t height
// ) {
// assertNotNull(framebuffer, "Framebuffer cannot be NULL");
// assertTrue(width > 0 && height > 0, "Width & height must be greater than 0");
// memoryZero(framebuffer, sizeof(framebuffer_t));
// textureInit(&framebuffer->texture, width, height, GL_RGBA, NULL);
// // Generate the framebuffer object using EXT
// glGenFramebuffersEXT(1, &framebuffer->id);
// glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
// // Attach the texture to the framebuffer
// glFramebufferTexture2DEXT(
// GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
// GL_TEXTURE_2D, framebuffer->texture.id, 0
// );
// // Check if the framebuffer is complete
// if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
// assertUnreachable("Framebuffer is not complete");
// }
// // Unbind the framebuffer
// glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// }
// void frameBufferBind(const framebuffer_t *framebuffer) {
// if(framebuffer == NULL) {
// glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
// return;
// }
// // Bind the framebuffer for rendering
// glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
// }
// void frameBufferDispose(framebuffer_t *framebuffer) {
// assertNotNull(framebuffer, "Framebuffer cannot be NULL");
// glDeleteFramebuffersEXT(1, &framebuffer->id);
// textureDispose(&framebuffer->texture);
// }
// #endif
}

View File

@@ -6,23 +6,43 @@
*/
#pragma once
#include "display/display.h"
#include "display/texture/texture.h"
#define FRAMEBUFFER_CLEAR_COLOR (1 << 0)
#define FRAMEBUFFER_CLEAR_DEPTH (1 << 1)
typedef struct {
#if DISPLAY_SDL2
#if DISPLAY_SDL2 == 1
// OpenGL Framebuffer Object ID
GLuint id;
texture_t texture;
#else
#error "Framebuffers not implemented on this platform."
#endif
} framebuffer_t;
extern framebuffer_t FRAMEBUFFER_BACKBUFFER;
extern const framebuffer_t *FRAMEBUFFER_BOUND;
void frameBufferInitBackbuffer();
/**
* Initializes the backbuffer framebuffer.
*/
void frameBufferInitBackbuffer(void);
/**
* Initializes a framebuffer.
*
* @param framebuffer The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
*/
#if DISPLAY_SIZE_DYNAMIC == 1
void frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
);
#endif
/**
* Gets the width of the framebuffer.

112
src/display/screen.c Normal file
View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "screen.h"
#include "assert/assert.h"
#include "display/spritebatch/spritebatch.h"
screen_t SCREEN;
void screenInit(void) {
// Virtual backbuffer for dynamic resolution scaling
#if DISPLAY_SIZE_DYNAMIC == 1
frameBufferInit(&SCREEN.frameBuffer, DISPLAY_WIDTH, DISPLAY_HEIGHT);
cameraInit(&SCREEN.frameBufferCamera);
SCREEN.frameBufferCamera.projType = CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC;
SCREEN.frameBufferCamera.viewType = CAMERA_VIEW_TYPE_MATRIX;
glm_lookat(
(vec3){0.0f, 0.0f, 1.0f},
(vec3){0.0f, 0.0f, 0.0f},
(vec3){0.0f, 1.0f, 0.0f},
SCREEN.frameBufferCamera.view
);
SCREEN.frameBufferCamera.nearClip = -1.0f;
SCREEN.frameBufferCamera.farClip = 1.0f;
#endif
}
void screenBind(void) {
#if DISPLAY_SIZE_DYNAMIC == 1
frameBufferBind(&SCREEN.frameBuffer);
#else
frameBufferBind(NULL);
#endif
frameBufferClear(
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
COLOR_CORNFLOWER_BLUE
);
}
void screenUnbindAndRender(void) {
assertTrue(SPRITEBATCH.spriteCount == 0, "Sprite batch not flushed");
// Render to real backbuffer
#if DISPLAY_SIZE_DYNAMIC == 1
frameBufferBind(NULL);
frameBufferClear(
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
COLOR_BLACK
);
SCREEN.frameBufferCamera.orthographic.left = 0;
SCREEN.frameBufferCamera.orthographic.right = frameBufferGetWidth(
FRAMEBUFFER_BOUND
);
SCREEN.frameBufferCamera.orthographic.bottom = frameBufferGetHeight(
FRAMEBUFFER_BOUND
);
SCREEN.frameBufferCamera.orthographic.top = 0;
cameraPushMatrix(&SCREEN.frameBufferCamera);
vec2 backbuffer = {
(float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND),
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
};
vec2 virtual = {
(float_t)frameBufferGetWidth(&SCREEN.frameBuffer),
(float_t)frameBufferGetHeight(&SCREEN.frameBuffer)
};
// Compare aspect ratios.
vec4 viewport;
float_t backbufferAspect = backbuffer[0] / backbuffer[1];
float_t virtualAspect = virtual[0] / virtual[1];
if (backbufferAspect > virtualAspect) {
// Backbuffer is wider: pillarbox
float_t scale = backbuffer[1] / virtual[1];
float_t width = virtual[0] * scale;
viewport[0] = (backbuffer[0] - width) * 0.5f;
viewport[1] = 0;
viewport[2] = width;
viewport[3] = backbuffer[1];
} else {
// Backbuffer is taller: letterbox
float_t scale = backbuffer[0] / virtual[0];
float_t height = virtual[1] * scale;
viewport[0] = 0;
viewport[1] = (backbuffer[1] - height) * 0.5f;
viewport[2] = backbuffer[0];
viewport[3] = height;
}
spriteBatchPush(
&SCREEN.frameBuffer.texture,
viewport[0], viewport[1],
viewport[0] + viewport[2], viewport[1] + viewport[3],
COLOR_WHITE,
0.0f, 1.0f, 1.0f, 0.0f
);
spriteBatchFlush();
cameraPopMatrix();
#endif
}
void screenDispose(void) {
frameBufferDispose(&SCREEN.frameBuffer);
}

41
src/display/screen.h Normal file
View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/camera.h"
#include "display/framebuffer/framebuffer.h"
typedef struct {
#if DISPLAY_SIZE_DYNAMIC == 1
framebuffer_t frameBuffer;
camera_t frameBufferCamera;
#else
void *empty;
#endif
} screen_t;
extern screen_t SCREEN;
/**
* Initializes the screen.
*/
void screenInit(void);
/**
* Binds the screen for rendering.
*/
void screenBind(void);
/**
* Unbinds the screen and renders it.
*/
void screenUnbindAndRender(void);
/**
* Disposes of the screen.
*/
void screenDispose(void);

View File

@@ -44,7 +44,6 @@ void textureInit(
switch(format) {
case TEXTURE_FORMAT_RGBA:
assertNotNull(data.rgba.colors, "RGBA texture data cannot be NULL");
glTexImage2D(
GL_TEXTURE_2D, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, (void*)data.rgba.colors
@@ -52,7 +51,6 @@ void textureInit(
break;
case TEXTURE_FORMAT_ALPHA:
assertNotNull(data.alpha.data, "Alpha texture data cannot be NULL");
glTexImage2D(
GL_TEXTURE_2D, 0, format, width, height, 0,
format, GL_UNSIGNED_BYTE, (void*)data.alpha.data

View File

@@ -6,8 +6,8 @@
*/
#pragma once
#include "display/display.h"
#include "display/color.h"
#include "display/displaydefs.h"
typedef enum {
#if DISPLAY_SDL2