prog
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
screen.c
|
||||
)
|
||||
@@ -0,0 +1,403 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "screen.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/memory.h"
|
||||
#include "display/mesh/quad.h"
|
||||
#include "display/shader/shaderunlit.h"
|
||||
|
||||
screen_t SCREEN;
|
||||
|
||||
errorret_t screenInit() {
|
||||
memoryZero(&SCREEN, sizeof(screen_t));
|
||||
|
||||
SCREEN.background = COLOR_CORNFLOWER_BLUE;
|
||||
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
SCREEN.mode = SCREEN_MODE_FIXED_VIEWPORT_HEIGHT;
|
||||
SCREEN.fixedHeight.height = DUSK_DISPLAY_SCREEN_HEIGHT;
|
||||
|
||||
quadBuffer(
|
||||
SCREEN.frameBufferMeshVertices,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
0.0f, 0.0f,
|
||||
1.0f, 1.0f
|
||||
#if MESH_ENABLE_COLOR
|
||||
, COLOR_WHITE
|
||||
#endif
|
||||
);
|
||||
errorChain(meshInit(
|
||||
&SCREEN.frameBufferMesh,
|
||||
QUAD_PRIMITIVE_TYPE,
|
||||
QUAD_VERTEX_COUNT,
|
||||
SCREEN.frameBufferMeshVertices
|
||||
));
|
||||
#endif
|
||||
|
||||
// Init screen to backbuffer mode by default
|
||||
errorChain(screenBind());
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t screenBind() {
|
||||
// Assume backbuffer is currently bound.
|
||||
switch(SCREEN.mode) {
|
||||
case SCREEN_MODE_BACKBUFFER: {
|
||||
// Screen mode backbuffer uses the full display size
|
||||
SCREEN.width = frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
SCREEN.height = frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
SCREEN.aspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
|
||||
|
||||
// No needd for a framebuffer.
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
if(SCREEN.framebufferReady) {
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
case SCREEN_MODE_FIXED_SIZE: {
|
||||
SCREEN.width = SCREEN.fixedSize.width;
|
||||
SCREEN.height = SCREEN.fixedSize.height;
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
|
||||
if(SCREEN.framebufferReady) {
|
||||
// Is current framebuffer the correct size?
|
||||
int32_t curFbWidth, curFbHeight;
|
||||
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
|
||||
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
|
||||
if(curFbWidth == SCREEN.width && curFbHeight == SCREEN.height) {
|
||||
// Correct size, nothing to do.
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Need a new framebuffer.
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
|
||||
// Create new framebuffer
|
||||
errorChain(frameBufferInit(
|
||||
&SCREEN.framebuffer, SCREEN.width, SCREEN.height
|
||||
));
|
||||
SCREEN.framebufferReady = true;
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_MODE_ASPECT_RATIO: {
|
||||
// Aspect ratio mode, requires a framebuffer.
|
||||
int32_t fbWidth, fbHeight;
|
||||
fbWidth = frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
fbHeight = frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
float_t currentAspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
|
||||
if(currentAspect == SCREEN.aspectRatio.ratio) {
|
||||
// No need to use framebuffer.
|
||||
SCREEN.width = fbWidth;
|
||||
SCREEN.height = fbHeight;
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
|
||||
if(SCREEN.framebufferReady) {
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
int32_t newFbWidth, newFbHeight;
|
||||
if(currentAspect > SCREEN.aspectRatio.ratio) {
|
||||
// Wider than target aspect, limit by height
|
||||
newFbWidth = (int32_t)floorf(fbHeight * SCREEN.aspectRatio.ratio);
|
||||
newFbHeight = (int32_t)fbHeight;
|
||||
} else {
|
||||
// Taller than target aspect, limit by width
|
||||
newFbHeight = (int32_t)floorf(fbWidth / SCREEN.aspectRatio.ratio);
|
||||
newFbWidth = (int32_t)fbWidth;
|
||||
}
|
||||
|
||||
if(SCREEN.framebufferReady) {
|
||||
// Is current framebuffer the correct size?
|
||||
int32_t curFbWidth, curFbHeight;
|
||||
float_t curFbAspect = frameBufferGetAspect(&SCREEN.framebuffer);
|
||||
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
|
||||
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
|
||||
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
|
||||
// Correct size, nothing to do.
|
||||
SCREEN.width = newFbWidth;
|
||||
SCREEN.height = newFbHeight;
|
||||
SCREEN.aspect = curFbAspect;
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Need a new framebuffer.
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
|
||||
// Create new framebuffer
|
||||
errorChain(frameBufferInit(
|
||||
&SCREEN.framebuffer, newFbWidth, newFbHeight
|
||||
));
|
||||
SCREEN.width = newFbWidth;
|
||||
SCREEN.height = newFbHeight;
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
SCREEN.framebufferReady = true;
|
||||
|
||||
// Bind FB
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_MODE_FIXED_HEIGHT: {
|
||||
float_t fbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
float_t fbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
float_t fbAspect = fbWidth / fbHeight;
|
||||
|
||||
int32_t newFbWidth, newFbHeight;
|
||||
newFbHeight = SCREEN.fixedHeight.height;
|
||||
newFbWidth = (int32_t)floorf(newFbHeight * fbAspect);
|
||||
|
||||
SCREEN.width = newFbWidth;
|
||||
SCREEN.height = newFbHeight;
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
|
||||
if(fbWidth == newFbWidth && fbHeight == newFbHeight) {
|
||||
// No need to use framebuffer.
|
||||
if(SCREEN.framebufferReady) {
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
if(SCREEN.framebufferReady) {
|
||||
// Is current framebuffer the correct size?
|
||||
int32_t curFbWidth, curFbHeight;
|
||||
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
|
||||
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
|
||||
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Need a new framebuffer.
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
|
||||
// Create a new framebuffer.
|
||||
errorChain(frameBufferInit(
|
||||
&SCREEN.framebuffer, newFbWidth, newFbHeight
|
||||
));
|
||||
SCREEN.framebufferReady = true;
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_MODE_FIXED_WIDTH: {
|
||||
float_t fbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
float_t fbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
float_t fbAspect = fbWidth / fbHeight;
|
||||
|
||||
int32_t newFbWidth, newFbHeight;
|
||||
newFbWidth = SCREEN.fixedWidth.width;
|
||||
newFbHeight = (int32_t)floorf(newFbWidth / fbAspect);
|
||||
|
||||
SCREEN.width = newFbWidth;
|
||||
SCREEN.height = newFbHeight;
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
|
||||
if(fbWidth == newFbWidth && fbHeight == newFbHeight) {
|
||||
// No need to use framebuffer.
|
||||
if(SCREEN.framebufferReady) {
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
if(SCREEN.framebufferReady) {
|
||||
// Is current framebuffer the correct size?
|
||||
int32_t curFbWidth, curFbHeight;
|
||||
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
|
||||
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
|
||||
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Need a new framebuffer.
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
|
||||
// Create a new framebuffer.
|
||||
errorChain(frameBufferInit(
|
||||
&SCREEN.framebuffer, newFbWidth, newFbHeight
|
||||
));
|
||||
SCREEN.framebufferReady = true;
|
||||
errorChain(frameBufferBind(&SCREEN.framebuffer));
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_MODE_FIXED_VIEWPORT_HEIGHT: {
|
||||
SCREEN.height = SCREEN.fixedViewportHeight.height;
|
||||
float_t fbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
float_t fbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
float_t fbAspect = fbWidth / fbHeight;
|
||||
SCREEN.width = (int32_t)floorf(SCREEN.height * fbAspect);
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
default: {
|
||||
assertUnreachable("Invalid screen mode.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t screenUnbind() {
|
||||
switch(SCREEN.mode) {
|
||||
// Nothing to do here.
|
||||
case SCREEN_MODE_BACKBUFFER:
|
||||
break;
|
||||
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
case SCREEN_MODE_ASPECT_RATIO:
|
||||
case SCREEN_MODE_FIXED_HEIGHT:
|
||||
case SCREEN_MODE_FIXED_SIZE:
|
||||
case SCREEN_MODE_FIXED_WIDTH:
|
||||
if(SCREEN.framebufferReady) {
|
||||
errorChain(frameBufferBind(NULL));
|
||||
}
|
||||
break;
|
||||
|
||||
case SCREEN_MODE_FIXED_VIEWPORT_HEIGHT:
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
assertUnreachable("Invalid screen mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t screenRender() {
|
||||
if(SCREEN.mode == SCREEN_MODE_BACKBUFFER) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
if(SCREEN.mode == SCREEN_MODE_FIXED_VIEWPORT_HEIGHT) {
|
||||
glViewport(0, 0, SCREEN.width, SCREEN.height);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
if(
|
||||
SCREEN.mode == SCREEN_MODE_ASPECT_RATIO ||
|
||||
SCREEN.mode == SCREEN_MODE_FIXED_HEIGHT ||
|
||||
SCREEN.mode == SCREEN_MODE_FIXED_SIZE ||
|
||||
SCREEN.mode == SCREEN_MODE_FIXED_WIDTH
|
||||
) {
|
||||
if(!SCREEN.framebufferReady) {
|
||||
// Nothing to do here.
|
||||
errorOk();
|
||||
}
|
||||
|
||||
float_t bbWidth, bbHeight;
|
||||
bbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
bbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
|
||||
float_t backBufferAspect = bbWidth / bbHeight;
|
||||
|
||||
// Determine framebuffer centering
|
||||
float_t fbWidth, fbHeight, fbAspect;
|
||||
float_t fbX, fbY;
|
||||
|
||||
fbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
|
||||
fbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
|
||||
fbAspect = fbWidth / fbHeight;
|
||||
|
||||
if(backBufferAspect > fbAspect) {
|
||||
fbHeight = bbHeight;
|
||||
fbWidth = fbHeight * fbAspect;
|
||||
fbX = (bbWidth - fbWidth) * 0.5f;
|
||||
fbY = 0.0f;
|
||||
} else {
|
||||
fbWidth = bbWidth;
|
||||
fbHeight = fbWidth / fbAspect;
|
||||
fbX = 0.0f;
|
||||
fbY = (bbHeight - fbHeight) * 0.5f;
|
||||
}
|
||||
|
||||
// Determine back buffer matricies
|
||||
float_t centerX = bbWidth * 0.5f;
|
||||
float_t centerY = bbHeight * 0.5f;
|
||||
mat4 view, proj, model;
|
||||
glm_ortho(
|
||||
0.0f, bbWidth, bbHeight, 0.0f, 0.01f, 1.0f,
|
||||
proj
|
||||
);
|
||||
glm_mat4_identity(view);
|
||||
glm_mat4_identity(model);
|
||||
|
||||
quadBuffer(
|
||||
SCREEN.frameBufferMeshVertices,
|
||||
centerX - fbWidth * 0.5f, centerY + fbHeight * 0.5f, // top-left
|
||||
centerX + fbWidth * 0.5f, centerY - fbHeight * 0.5f, // bottom-right
|
||||
0.0f, 0.0f,
|
||||
1.0f, 1.0f
|
||||
#if MESH_ENABLE_COLOR
|
||||
, COLOR_WHITE
|
||||
#endif
|
||||
);
|
||||
|
||||
frameBufferClear(
|
||||
FRAMEBUFFER_CLEAR_COLOR | FRAMEBUFFER_CLEAR_DEPTH,
|
||||
COLOR_BLACK
|
||||
);
|
||||
|
||||
shaderBind(&SHADER_UNLIT);
|
||||
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj);
|
||||
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view);
|
||||
shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model);
|
||||
|
||||
// errorChain(textureBind(&SCREEN.framebuffer.texture));
|
||||
errorChain(meshDraw(&SCREEN.frameBufferMesh, 0, -1));
|
||||
|
||||
errorOk();
|
||||
};
|
||||
#endif
|
||||
|
||||
assertUnreachable("Invalid screen mode.");
|
||||
errorThrow("Invalid screen mode.");
|
||||
}
|
||||
|
||||
errorret_t screenDispose() {
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
if(SCREEN.framebufferReady) {
|
||||
errorChain(frameBufferDispose(&SCREEN.framebuffer));
|
||||
SCREEN.framebufferReady = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "display/framebuffer/framebuffer.h"
|
||||
#include "display/mesh/quad.h"
|
||||
#include "display/color.h"
|
||||
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
#ifndef DUSK_DISPLAY_SCREEN_HEIGHT
|
||||
#error "DUSK_DISPLAY_SCREEN_HEIGHT must be defined"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SCREEN_MODE_BACKBUFFER,
|
||||
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
SCREEN_MODE_FIXED_SIZE,
|
||||
SCREEN_MODE_ASPECT_RATIO,// Maintains aspect at all cost
|
||||
SCREEN_MODE_FIXED_HEIGHT, // Fixed height, width expands/contracts as needed
|
||||
SCREEN_MODE_FIXED_WIDTH, // Fixed width, height expands/contracts as needed
|
||||
// Fixed viewport height. Fixed height but higher resolution.
|
||||
SCREEN_MODE_FIXED_VIEWPORT_HEIGHT,
|
||||
#endif
|
||||
} screenmode_t;
|
||||
|
||||
// typedef enum {
|
||||
// SCREEN_SCALE_MODE_FILL,
|
||||
// SCREEN_SCALE_MODE_INTEGER,
|
||||
// SCREEN_SCALE_MODE_INEGER_OVERFLOW
|
||||
// } screenscalemode_t;
|
||||
|
||||
typedef struct {
|
||||
screenmode_t mode;
|
||||
// screenscalemode_t scaleMode;
|
||||
|
||||
// Calculated dimensions of the viewport, to be used by the camera
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
float_t aspect;
|
||||
color_t background;
|
||||
|
||||
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
|
||||
framebuffer_t framebuffer;
|
||||
bool_t framebufferReady;
|
||||
// camera_t framebufferCamera;
|
||||
mesh_t frameBufferMesh;
|
||||
meshvertex_t frameBufferMeshVertices[QUAD_VERTEX_COUNT];
|
||||
#endif
|
||||
|
||||
union {
|
||||
struct {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
} fixedSize;
|
||||
|
||||
struct {
|
||||
float_t ratio;
|
||||
} aspectRatio;
|
||||
|
||||
struct {
|
||||
int32_t height;
|
||||
} fixedHeight;
|
||||
|
||||
struct {
|
||||
int32_t width;
|
||||
} fixedWidth;
|
||||
|
||||
struct {
|
||||
int32_t height;
|
||||
} fixedViewportHeight;
|
||||
};
|
||||
} screen_t;
|
||||
|
||||
extern screen_t SCREEN;
|
||||
|
||||
/**
|
||||
* Initializes the screen system.
|
||||
*
|
||||
* @return Error code and state, if error occurs.
|
||||
*/
|
||||
errorret_t screenInit();
|
||||
|
||||
/**
|
||||
* Binds the screen, this is done before rendering game content.
|
||||
*
|
||||
* @return Error code and state, if error occurs.
|
||||
*/
|
||||
errorret_t screenBind();
|
||||
|
||||
/**
|
||||
* Unbinds the screen, does nothing for now.
|
||||
*
|
||||
* @return Error code and state, if error occurs.
|
||||
*/
|
||||
errorret_t screenUnbind();
|
||||
|
||||
/**
|
||||
* Renders the screen to the current framebuffer.
|
||||
*
|
||||
* @return Error code and state, if error occurs.
|
||||
*/
|
||||
errorret_t screenRender();
|
||||
|
||||
/**
|
||||
* Disposes the screen system.
|
||||
*
|
||||
* @return Error code and state, if error occurs.
|
||||
*/
|
||||
errorret_t screenDispose();
|
||||
Reference in New Issue
Block a user