Files
dusk/src/duskraylib/display/renderimpl.c
2025-06-11 21:07:07 -05:00

208 lines
5.5 KiB
C

/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "display/render.h"
#include "assert/assert.h"
#include "util/memory.h"
uint8_t RENDER_BACKGROUND_X = 0;
uint8_t RENDER_BACKGROUND_Y = 0;
uint8_t RENDER_STATUS = 0;
bool_t RENDER_DISPLAY_ON = false;
uint8_t RENDER_TILES[RENDER_TILE_COUNT] = { 0 };
uint8_t RENDER_BACKGROUND_TILEMAP[RENDER_BACKGROUND_TILE_COUNT] = { 0 };
Color RENDER_PALETTE[RENDER_PALETTE_COLOR_COUNT] = {
{ 102, 191, 47, 255 },
{ 78, 146, 35, 255 },
{ 48, 89, 22, 255 },
{ 18, 33, 8, 255 }
};
RenderTexture2D RENDER_BACKBUFFER;
RenderTexture2D RENDER_TILES_TEXTURE;
void renderInit(void) {
InitWindow(RENDER_WIDTH_PIXELS * 4, RENDER_HEIGHT_PIXELS * 4, "Dusk");
SetTargetFPS(60);
SetWindowState(
FLAG_WINDOW_RESIZABLE |
FLAG_WINDOW_HIGHDPI |
FLAG_VSYNC_HINT
);
// Create back buffer for rendering.
RENDER_BACKBUFFER = LoadRenderTexture(
RENDER_WIDTH_PIXELS,
RENDER_HEIGHT_PIXELS
);
// Create texture to hold the tile data.
RENDER_TILES_TEXTURE = LoadRenderTexture(
RENDER_TILE_COUNT * RENDER_TILE_WIDTH,
RENDER_TILE_HEIGHT
);
}
void renderVsync() {
int32_t x, y, i;
BeginDrawing();
if(RENDER_DISPLAY_ON) {
// Update the texture with the new tile data
BeginTextureMode(RENDER_TILES_TEXTURE);
i = 0;
for(i = 0; i < RENDER_TILE_COUNT; i++) {
uint8_t *tile = RENDER_TILES + (i * RENDER_TILE_BYTES_PER_TILE);
// For each pixel in the tile...
for(y = 0; y < RENDER_TILE_HEIGHT; y++) {
uint8_t low = tile[y * RENDER_TILE_BYTES_PER_ROW];
uint8_t high = tile[y * RENDER_TILE_BYTES_PER_ROW + 1];
for(x = 0; x < RENDER_TILE_WIDTH; x++) {
uint8_t loBit = (low >> (7 - x)) & 1;
uint8_t hiBit = (high >> (7 - x)) & 1;
uint8_t paletteIndex = (hiBit << 1) | loBit;
// Draw the pixel to the texture
DrawPixel(
(i * RENDER_TILE_WIDTH) + x,
y,
RENDER_PALETTE[paletteIndex]
);
}
}
}
EndTextureMode();
// Clear the back buffer
BeginTextureMode(RENDER_BACKBUFFER);
ClearBackground(RENDER_PALETTE[0]);
// Render background tiles
i = 0;
for(y = 0; y < RENDER_BACKGROUND_ROWS; y++) {
for(x = 0; x < RENDER_BACKGROUND_COLUMNS; x++) {
// Get the tile index from the tilemap
uint8_t tileIndex = RENDER_BACKGROUND_TILEMAP[i++];
DrawTexturePro(
RENDER_TILES_TEXTURE.texture,
(Rectangle){
.x = ((int32_t)tileIndex) * RENDER_TILE_WIDTH,
.y = 0,
.width = RENDER_TILE_WIDTH,
.height = -RENDER_TILE_HEIGHT
},
(Rectangle){
((int32_t)x * RENDER_TILE_WIDTH) - RENDER_BACKGROUND_X,
((int32_t)y * RENDER_TILE_HEIGHT) - RENDER_BACKGROUND_Y,
RENDER_TILE_WIDTH,
RENDER_TILE_HEIGHT
},
(Vector2){ 0, 0 },
0.0f,
WHITE
);
}
}
// Render the back buffer to the screen
EndTextureMode();
ClearBackground(WHITE);
// Keep aspect and center the render
int32_t renderWidth, renderHeight, renderX, renderY;
const int32_t width = GetScreenWidth();
const int32_t height = GetScreenHeight();
if (RENDER_WIDTH_PIXELS * height > RENDER_HEIGHT_PIXELS * width) {
renderWidth = width;
renderHeight = (RENDER_HEIGHT_PIXELS * width) / RENDER_WIDTH_PIXELS;
renderX = 0;
renderY = (height - renderHeight) / 2;
} else {
renderWidth = (RENDER_WIDTH_PIXELS * height) / RENDER_HEIGHT_PIXELS;
renderHeight = height;
renderX = (width - renderWidth) / 2;
renderY = 0;
}
DrawTexturePro(
RENDER_BACKBUFFER.texture,
(Rectangle) { 0, 0, RENDER_WIDTH_PIXELS, -RENDER_HEIGHT_PIXELS },
(Rectangle) { renderX, renderY, renderWidth, renderHeight },
(Vector2) { 0, 0 },
0.0f,
WHITE
);
}
EndDrawing();
if(WindowShouldClose()) RENDER_STATUS |= RENDER_SHOULD_EXIT;
}
void renderDispose() {
UnloadRenderTexture(RENDER_TILES_TEXTURE);
CloseWindow();
}
void renderDisplayOn(void) {
RENDER_DISPLAY_ON = true;
}
void renderDisplayOff(void) {
RENDER_DISPLAY_ON = false;
}
void renderTilesBuffer(
const uint8_t index,
const uint8_t count,
const uint8_t *tiles
) {
assertTrue(count > 0, "Count must be greater than zero");
assertNotNull(tiles, "Tiles pointer must not be null");
// Copy data to fake vram
memoryCopy(
&RENDER_TILES[index * RENDER_TILE_BYTES_PER_TILE],
tiles,
count * RENDER_TILE_BYTES_PER_TILE
);
}
void renderBackgroundTilesBufferRectangle(
const uint8_t xPos,
const uint8_t yPos,
const uint8_t width,
const uint8_t height,
const uint8_t *tiles
) {
uint8_t w = width, h = height, x = xPos, y = yPos;
assertNotNull(tiles, "Tiles pointer must not be null");
// Clamp x and y to tilemap bounds
if (x >= RENDER_BACKGROUND_COLUMNS) x = RENDER_BACKGROUND_COLUMNS - 1;
if (y >= RENDER_BACKGROUND_ROWS) y = RENDER_BACKGROUND_ROWS - 1;
// Clamp width and height so we don't overflow past edges
if (x + w > RENDER_BACKGROUND_COLUMNS) {
w = RENDER_BACKGROUND_COLUMNS - x;
}
if (y + h > RENDER_BACKGROUND_ROWS) {
h = RENDER_BACKGROUND_ROWS - y;
}
if (w == 0 || h == 0) return;
for (uint8_t row = 0; row < h; row++) {
memoryCopy(
&RENDER_BACKGROUND_TILEMAP[(y + row) * RENDER_BACKGROUND_COLUMNS + x],
&tiles[row * width],
w
);
}
}