Template
first draft version of the mini engine
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
CompileFlags:
|
||||
Add:
|
||||
- "-I/home/imp/Code/c_sdl_playground/include"
|
||||
- "-std=c11"
|
||||
@@ -1,7 +1,10 @@
|
||||
CC := gcc
|
||||
LIBS := `pkg-config --libs sdl3`
|
||||
INC := `pkg-config --cflags sdl3`
|
||||
SRC := src/main.c
|
||||
INC := `pkg-config --cflags sdl3` -I./include
|
||||
SRC := src/main.c \
|
||||
src/engine/engine.c \
|
||||
src/engine/engine_init.c \
|
||||
src/engine/render.c
|
||||
OUT := build/main
|
||||
|
||||
build:
|
||||
|
||||
+2092
-30
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
-std=c11
|
||||
-Wall
|
||||
-Wextra
|
||||
-Iinclude
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define WINDOW_WIDTH 1280
|
||||
#define WINDOW_HEIGHT 720
|
||||
#define DEFAULT_WINDOW_NAME "SDL Playground"
|
||||
#define WINDOW_NAME_LENGTH 128
|
||||
#define TARGET_FRAME 1.0 / 60.0
|
||||
|
||||
typedef struct {
|
||||
char window_name[WINDOW_NAME_LENGTH];
|
||||
uint16_t width, height;
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
} engine_sdl_state_t;
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
* this function runs inside of the SDL_PollEvent loop
|
||||
* useful for user input, quitting the program and so on
|
||||
*/
|
||||
void (*engine_poll_events)(void);
|
||||
/**
|
||||
* meant as the "area" for manupulating the framebuffer;
|
||||
* after this is executed, the contents of
|
||||
* game_state->framebuffer get drawn
|
||||
*/
|
||||
void (*engine_update_framebuffer)(void);
|
||||
engine_sdl_state_t *sdl_state;
|
||||
} engine_internal_state_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t framebuffer[WINDOW_WIDTH * WINDOW_HEIGHT];
|
||||
SDL_Event event;
|
||||
bool is_running;
|
||||
uint16_t width, height;
|
||||
} game_state_t;
|
||||
|
||||
extern engine_internal_state_t *internal_state;
|
||||
extern game_state_t *game_state;
|
||||
|
||||
engine_sdl_state_t*
|
||||
engine_sdl_init();
|
||||
|
||||
void
|
||||
engine_sdl_destroy(
|
||||
engine_sdl_state_t *state
|
||||
);
|
||||
|
||||
bool
|
||||
engine_init(
|
||||
void (*engine_poll_events)(void),
|
||||
void (*engine_update_framebuffer)(void)
|
||||
);
|
||||
|
||||
void
|
||||
engine_start();
|
||||
|
||||
void
|
||||
engine_stop();
|
||||
|
||||
void
|
||||
put_pixel(
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
uint32_t color
|
||||
);
|
||||
|
||||
void
|
||||
clear_framebuffer(
|
||||
uint32_t color
|
||||
);
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define eprintf(fmt, ...) \
|
||||
fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define RCODE_FAILURE false
|
||||
#define RCODE_SUCCESS true
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "engine.h"
|
||||
|
||||
engine_internal_state_t *internal_state = NULL;
|
||||
game_state_t *game_state = NULL;
|
||||
|
||||
void
|
||||
engine_start() {
|
||||
// main game loop
|
||||
while(game_state->is_running) {
|
||||
uint64_t start = SDL_GetPerformanceCounter();
|
||||
|
||||
// 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
|
||||
SDL_UpdateTexture(
|
||||
internal_state->sdl_state->texture,
|
||||
NULL, game_state->framebuffer,
|
||||
internal_state->sdl_state->width * sizeof(uint32_t)
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
uint64_t end = SDL_GetPerformanceCounter();
|
||||
|
||||
double elapsed =
|
||||
(double)(end - start) /
|
||||
(double)SDL_GetPerformanceFrequency();
|
||||
|
||||
// cap the framerate at '1.0/TARGET_FRAME' fps
|
||||
if (elapsed < TARGET_FRAME) {
|
||||
SDL_Delay((TARGET_FRAME - elapsed) * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
engine_stop() {
|
||||
engine_sdl_destroy(internal_state->sdl_state);
|
||||
free(internal_state);
|
||||
free(game_state);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "engine.h"
|
||||
#include "error.h"
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <SDL3/SDL_video.h>
|
||||
|
||||
bool
|
||||
engine_sdl_init_state(engine_sdl_state_t **state) {
|
||||
*state = (engine_sdl_state_t *)
|
||||
malloc(sizeof(engine_sdl_state_t));
|
||||
|
||||
if (*state == NULL) {
|
||||
eprintf("failed to initialize engine_sdl_state");
|
||||
return RCODE_FAILURE;
|
||||
}
|
||||
|
||||
(*state)->width = WINDOW_WIDTH;
|
||||
(*state)->height = WINDOW_HEIGHT;
|
||||
strncpy(
|
||||
(*state)->window_name, DEFAULT_WINDOW_NAME,
|
||||
WINDOW_NAME_LENGTH
|
||||
);
|
||||
|
||||
return RCODE_SUCCESS;
|
||||
}
|
||||
|
||||
bool
|
||||
engine_sdl_init_root()
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
eprintf("SDL_Init failed! %s\n", SDL_GetError());
|
||||
return RCODE_FAILURE;
|
||||
} else {
|
||||
return RCODE_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
engine_sdl_init_window(
|
||||
engine_sdl_state_t* state
|
||||
) {
|
||||
state->window = SDL_CreateWindow(
|
||||
state->window_name,
|
||||
state->width, state->height, 0
|
||||
);
|
||||
|
||||
if (state->window == NULL) {
|
||||
eprintf("SDL_CreateWindow failed! %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return RCODE_FAILURE;
|
||||
}
|
||||
|
||||
return RCODE_SUCCESS;
|
||||
}
|
||||
|
||||
bool
|
||||
engine_sdl_init_renderer(
|
||||
engine_sdl_state_t *state
|
||||
) {
|
||||
state->renderer = SDL_CreateRenderer(state->window, NULL);
|
||||
if (state->renderer == NULL) {
|
||||
eprintf("SDL_CreateRenderer failed! %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(state->window);
|
||||
SDL_Quit();
|
||||
return RCODE_FAILURE;
|
||||
}
|
||||
|
||||
return RCODE_SUCCESS;
|
||||
}
|
||||
|
||||
bool
|
||||
engine_sdl_init_texture(
|
||||
engine_sdl_state_t *state
|
||||
) {
|
||||
state->texture = SDL_CreateTexture(
|
||||
state->renderer, SDL_PIXELFORMAT_XRGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
state->width, state->height
|
||||
);
|
||||
if (state->texture == NULL) {
|
||||
eprintf("SDL_CreateTexture failed! %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(state->window);
|
||||
SDL_DestroyRenderer(state->renderer);
|
||||
SDL_Quit();
|
||||
return RCODE_FAILURE;
|
||||
}
|
||||
|
||||
return RCODE_SUCCESS;
|
||||
}
|
||||
|
||||
engine_sdl_state_t*
|
||||
engine_sdl_init()
|
||||
{
|
||||
engine_sdl_state_t *state;
|
||||
|
||||
if (!engine_sdl_init_state(&state) ||
|
||||
!engine_sdl_init_root() ||
|
||||
!engine_sdl_init_window(state) ||
|
||||
!engine_sdl_init_renderer(state) ||
|
||||
!engine_sdl_init_texture(state)
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* only call intentinally, not when engine_sdl_init fails
|
||||
* -> it cleans up after itself already
|
||||
*/
|
||||
void
|
||||
engine_sdl_destroy(
|
||||
engine_sdl_state_t *state
|
||||
) {
|
||||
SDL_DestroyTexture(state->texture);
|
||||
SDL_DestroyRenderer(state->renderer);
|
||||
SDL_DestroyWindow(state->window);
|
||||
SDL_Quit();
|
||||
free(state);
|
||||
}
|
||||
|
||||
bool
|
||||
engine_init(
|
||||
void (*engine_poll_events)(void),
|
||||
void (*engine_update_framebuffer)(void)
|
||||
) {
|
||||
internal_state = (engine_internal_state_t *)
|
||||
malloc(sizeof(engine_internal_state_t));
|
||||
|
||||
game_state = (game_state_t *)
|
||||
malloc(sizeof(game_state_t));
|
||||
|
||||
internal_state->engine_poll_events
|
||||
= engine_poll_events;
|
||||
|
||||
internal_state->engine_update_framebuffer
|
||||
= engine_update_framebuffer;
|
||||
|
||||
internal_state->sdl_state = engine_sdl_init();
|
||||
|
||||
game_state->is_running = true;
|
||||
game_state->width = internal_state->sdl_state->width;
|
||||
game_state->height = internal_state->sdl_state->height;
|
||||
|
||||
return (game_state == NULL || internal_state == NULL)
|
||||
? RCODE_FAILURE : RCODE_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#include "engine.h"
|
||||
#include "error.h"
|
||||
|
||||
// alter a pixel at an imaginary x & y coordinates
|
||||
void
|
||||
put_pixel(
|
||||
unsigned int x,
|
||||
unsigned int y,
|
||||
uint32_t color
|
||||
) {
|
||||
uint16_t w = game_state->width;
|
||||
uint16_t h = game_state->height;
|
||||
|
||||
if (x < 0 || x >= w || y < 0 || y >= h) {
|
||||
eprintf(
|
||||
"CRITICAL: attempted buffer overflow in put_pixel\n"
|
||||
"at x = %d, y = %d", x, y
|
||||
);
|
||||
} else {
|
||||
game_state->framebuffer[game_state->width * y + x]
|
||||
= color;
|
||||
}
|
||||
}
|
||||
|
||||
// fill the entire framebuffer with a given color
|
||||
void
|
||||
clear_framebuffer(
|
||||
uint32_t color
|
||||
) {
|
||||
uint16_t w = game_state->width;
|
||||
uint16_t h = game_state->height;
|
||||
|
||||
for (int i = 0; i < w * h; i++) {
|
||||
game_state->framebuffer[i] = color;
|
||||
}
|
||||
}
|
||||
|
||||
+36
-100
@@ -1,88 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include "engine.h"
|
||||
|
||||
#define WIDTH 320
|
||||
#define HEIGHT 200
|
||||
bool left = true;
|
||||
|
||||
uint32_t framebuffer[WIDTH * HEIGHT];
|
||||
// polling events every frame
|
||||
void
|
||||
client_poll_event() {
|
||||
SDL_Event ev = game_state->event;
|
||||
|
||||
// alter a pixel at an imaginary x & y coordinates
|
||||
void put_pixel(unsigned int x, unsigned int y, uint32_t color) {
|
||||
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) {
|
||||
fprintf(stderr, "CRITICAL: attempted buffer overflow in put_pixel\n"
|
||||
"at x = %d, y = %d", x, y);
|
||||
return;
|
||||
if (ev.type == SDL_EVENT_QUIT) {
|
||||
game_state->is_running = false;
|
||||
}
|
||||
|
||||
framebuffer[WIDTH * y + x] = color;
|
||||
}
|
||||
|
||||
// fill the entire framebuffer with a given color
|
||||
void clear_framebuffer(uint32_t color) {
|
||||
for (int i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
framebuffer[i] = color;
|
||||
if (ev.type == SDL_EVENT_KEY_DOWN) {
|
||||
switch (ev.key.key) {
|
||||
case SDLK_Q:
|
||||
game_state->is_running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
SDL_Event event;
|
||||
bool is_running = true;
|
||||
|
||||
const double target_frame = 1.0 / 60.0;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
fprintf(stderr, "SDL_Init failed! %s\n", SDL_GetError());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow(
|
||||
"SDL Framebuffer",
|
||||
WIDTH, HEIGHT, 0
|
||||
);
|
||||
if (window == NULL) {
|
||||
fprintf(stderr, "SDL_CreateWindow failed! %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
renderer = SDL_CreateRenderer(window, NULL);
|
||||
if (renderer == NULL) {
|
||||
fprintf(stderr, "SDL_CreateRenderer failed! %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
texture = SDL_CreateTexture(
|
||||
renderer, SDL_PIXELFORMAT_XRGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
WIDTH, HEIGHT
|
||||
);
|
||||
if (texture == NULL) {
|
||||
fprintf(stderr, "SDL_CreateTexture failed! %s\n", SDL_GetError());
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bool left = true;
|
||||
|
||||
while (is_running) {
|
||||
uint64_t start = SDL_GetPerformanceCounter();
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (event.type == SDL_EVENT_QUIT) {
|
||||
is_running = false;
|
||||
}
|
||||
}
|
||||
// performed every frame
|
||||
void
|
||||
client_update_framebuffer() {
|
||||
// draw a green square
|
||||
// clear_framebuffer(0x000000);
|
||||
// for (int i = 0; i < 20; i++) {
|
||||
// for (int j = 0; j < 20; j++) {
|
||||
// put_pixel(i, j, 0x00FF00);
|
||||
// }
|
||||
// }
|
||||
|
||||
// draw alternating green squares
|
||||
if (left) {
|
||||
@@ -103,28 +49,18 @@ int main() {
|
||||
left = true;
|
||||
}
|
||||
SDL_Delay(100);
|
||||
|
||||
SDL_UpdateTexture(texture, NULL, framebuffer, WIDTH * sizeof(uint32_t));
|
||||
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
uint64_t end = SDL_GetPerformanceCounter();
|
||||
|
||||
double elapsed = (double)(end - start) / (double)SDL_GetPerformanceFrequency();
|
||||
|
||||
// cap the framerate at '1.0/target_frame' fps
|
||||
if (elapsed < target_frame) {
|
||||
SDL_Delay((target_frame - elapsed) * 1000);
|
||||
}
|
||||
|
||||
int main() {
|
||||
// initialize the engine
|
||||
if (engine_init(
|
||||
client_poll_event,
|
||||
client_update_framebuffer
|
||||
)) {
|
||||
engine_start();
|
||||
engine_stop();
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_DestroyTexture(texture);
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
SDL_Quit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user