176 lines
3.4 KiB
C
176 lines
3.4 KiB
C
#include "engine.h"
|
|
|
|
engine_internal_state_t *internal_state = NULL;
|
|
game_state_t *game_state = NULL;
|
|
|
|
void
|
|
mark_dirty_rect(int x, int y, int w, int h)
|
|
{
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
// clip rectangle to the framebuffer
|
|
if (x < 0) {
|
|
w += x;
|
|
x = 0;
|
|
}
|
|
|
|
if (y < 0) {
|
|
h += y;
|
|
y = 0;
|
|
}
|
|
|
|
if (x + w > game_state->width) {
|
|
w = game_state->width - x;
|
|
}
|
|
|
|
if (y + h > game_state->height) {
|
|
h = game_state->height - y;
|
|
}
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
SDL_Rect rect = {
|
|
.x = x,
|
|
.y = y,
|
|
.w = w,
|
|
.h = h
|
|
};
|
|
|
|
if (!game_state->framebuffer_dirty) {
|
|
game_state->dirty_rect = rect;
|
|
game_state->framebuffer_dirty = true;
|
|
return;
|
|
}
|
|
|
|
int left = game_state->dirty_rect.x < rect.x
|
|
? game_state->dirty_rect.x
|
|
: rect.x;
|
|
|
|
int top = game_state->dirty_rect.y < rect.y
|
|
? game_state->dirty_rect.y
|
|
: rect.y;
|
|
|
|
int old_right =
|
|
game_state->dirty_rect.x + game_state->dirty_rect.w;
|
|
|
|
int new_right = rect.x + rect.w;
|
|
|
|
int old_bottom =
|
|
game_state->dirty_rect.y + game_state->dirty_rect.h;
|
|
|
|
int new_bottom = rect.y + rect.h;
|
|
|
|
int right = old_right > new_right
|
|
? old_right
|
|
: new_right;
|
|
|
|
int bottom = old_bottom > new_bottom
|
|
? old_bottom
|
|
: new_bottom;
|
|
|
|
game_state->dirty_rect = (SDL_Rect) {
|
|
.x = left,
|
|
.y = top,
|
|
.w = right - left,
|
|
.h = bottom - top
|
|
};
|
|
}
|
|
|
|
bool
|
|
upload_dirty_texture(void)
|
|
{
|
|
if (!game_state->framebuffer_dirty) {
|
|
return true;
|
|
}
|
|
|
|
SDL_Texture *texture =
|
|
internal_state->sdl_state->texture;
|
|
|
|
SDL_Rect rect = game_state->dirty_rect;
|
|
|
|
void *locked_pixels = NULL;
|
|
int locked_pitch = 0;
|
|
|
|
if (!SDL_LockTexture(
|
|
texture,
|
|
&rect,
|
|
&locked_pixels,
|
|
&locked_pitch
|
|
)) {
|
|
SDL_Log(
|
|
"SDL_LockTexture failed: %s",
|
|
SDL_GetError()
|
|
);
|
|
return false;
|
|
}
|
|
|
|
const size_t source_pitch =
|
|
(size_t)game_state->width * sizeof(uint32_t);
|
|
|
|
const size_t row_bytes =
|
|
(size_t)rect.w * sizeof(uint32_t);
|
|
|
|
const uint8_t *source =
|
|
(const uint8_t *)game_state->framebuffer +
|
|
(size_t)rect.y * source_pitch +
|
|
(size_t)rect.x * sizeof(uint32_t);
|
|
|
|
uint8_t *destination =
|
|
(uint8_t *)locked_pixels;
|
|
|
|
for (int y = 0; y < rect.h; ++y) {
|
|
memcpy(
|
|
destination + (size_t)y * locked_pitch,
|
|
source + (size_t)y * source_pitch,
|
|
row_bytes
|
|
);
|
|
}
|
|
|
|
SDL_UnlockTexture(texture);
|
|
|
|
game_state->framebuffer_dirty = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
engine_start() {
|
|
// main game loop
|
|
while(game_state->is_running) {
|
|
// poll events
|
|
while (SDL_PollEvent(&game_state->event)) {
|
|
internal_state->engine_poll_events();
|
|
}
|
|
|
|
// call the user-defined framebuffer manipulation function
|
|
internal_state->engine_update_framebuffer();
|
|
|
|
// update the texture with the modified framebuffer
|
|
upload_dirty_texture();
|
|
|
|
SDL_RenderClear(
|
|
internal_state->sdl_state->renderer
|
|
);
|
|
|
|
SDL_RenderTexture(
|
|
internal_state->sdl_state->renderer,
|
|
internal_state->sdl_state->texture,
|
|
NULL, NULL
|
|
);
|
|
|
|
SDL_RenderPresent(
|
|
internal_state->sdl_state->renderer
|
|
);
|
|
}
|
|
}
|
|
|
|
void
|
|
engine_stop() {
|
|
engine_sdl_destroy(internal_state->sdl_state);
|
|
free(internal_state);
|
|
free(game_state);
|
|
} |