208 lines
5.5 KiB
C
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
|
|
);
|
|
}
|
|
}
|