Added support for desktop full screen with pillarboxing and letterboxing

and started adding some comments to the code
This commit is contained in:
abdelrahman committed 2022-10-02 02:16:28 +01:00
1 parent ffaa6e5ae7
commit e110b1f2fd
10 files changed
+351 -77

No files matched your search

+22 -1
View File
@@ -46,6 +46,10 @@ struct asset_manager {
};
size_t hash_path(const char *path) {
/**
* Converts a string into a numerical index using a simple hashing algorithm
*/
size_t i = 0;
for (size_t j = 0; path[j]; ++j) {
@@ -125,6 +129,11 @@ void remove_id_node(IDNodeList *node_list, AssetID id) {
AssetID get_asset_id_from_id_node(AssetManager *am, IDNodeList *node_list,
const char *path) {
/**
* Checks to see if an asset with the same path exists and returns the ID of
* that asset if it exists, or an INVALID_ID otherwise
*/
if (node_list->node_count == 0) {
return INVALID_ID;
}
@@ -184,9 +193,14 @@ AssetID load_asset_to_manager(AssetManager *am, const char *path,
}
++(am->user_counter[existing_id]);
return existing_id;
}
if (am->count + 1 == MAX_ASSET_COUNT && am->deleted_count == 0) {
return INVALID_ID;
}
Asset asset = {};
bool loaded = true;
@@ -363,7 +377,14 @@ void destroy_asset_manager(AssetManager **am) {
return;
}
for (size_t i = 0; i < (*am)->count; ++i) {
// TODO (Abdelrahman): Figure out if there is a better way to remove all of
// the existing assets without having to loop for the maximum number of
// assets allowed.
for (size_t i = 0; i < MAX_ASSET_COUNT; ++i) {
if (!(*am)->assets[i].path) {
continue;
}
(*am)->user_counter[i] = 0;
size_t hash_index = hash_path((*am)->assets[i].path);
+82 -39
View File
@@ -18,6 +18,7 @@
#include "constants.h"
#include "engine_build_config.h"
#include "reusable_ids.h"
#include <SDL2/SDL_pixels.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -130,10 +131,32 @@ bool init_engine(const char *game_name, WindowSize window_size,
bool game_running() { return GameEngine.running; }
WindowSize get_window_defined_size() { return GameEngine.window.defined_size; }
void close_engine() {
destroy_asset_manager(&(GameEngine.asset_manager));
WindowSize get_window_drawable_size() {
return GameEngine.window.drawable_size;
destroy_object_store(&(GameEngine.object_store));
destroy_window(&(GameEngine.window));
GameEngine.last_time = 0.0f;
GameEngine.delta = 0.0f;
GameEngine.running = false;
Mix_CloseAudio();
Mix_Quit();
TTF_Quit();
IMG_Quit();
SDL_Quit();
#ifdef DEBUG_ENGINE
printf("Game closed successfully\n");
#endif /* DEBUG_ENGINE */
}
ObjectID add_object(void *object, EventHandlerPtr event_handler,
@@ -162,6 +185,62 @@ AssetID load_asset(const char *path, AssetType type) {
NULL);
}
void close_asset(AssetID *id) {
if (*id < 0) {
return;
}
remove_asset_from_manager(GameEngine.asset_manager, id);
}
WindowSize get_window_defined_size() { return GameEngine.window.defined_size; }
WindowSize get_window_real_size() { return GameEngine.window.real_size; }
WindowDrawingArea get_window_drawing_area() {
return GameEngine.window.drawing_area;
}
void fill_rectangle(float x, float y, float w, float h, SDL_Color color) {
fill_rect(GameEngine.window.renderer, color, x, y, w, h);
}
void clip_render_rect_to_drawing_area(SDL_FRect *rect) {
WindowDrawingArea area = get_window_drawing_area();
// If the rectangle is completely out of bounds, set its width and height to 0
if (rect->x > area.x + area.width || rect->x + rect->w < area.x ||
rect->y > area.y + area.height || rect->y + rect->h < area.y) {
rect->w = 0.0f;
rect->h = 0.0f;
return;
}
float x_diff = 0.0f;
float y_diff = 0.0f;
// If the rectangle is partially out of the horizontal bounds of the drawing
// area, calculate how much of its width will be clipped
if (rect->x < area.x) {
x_diff = area.x - rect->x;
rect->x += x_diff;
} else if (rect->x + rect->w > area.x + area.width) {
x_diff = (rect->x + rect->w) - (area.x + area.width);
}
// If the rectangle is partially out of the vertical bounds of the drawing
// area, calculate how much of its height will be clipped
if (rect->y < area.y) {
y_diff = area.y - rect->y;
rect->y += y_diff;
} else if (rect->y + rect->h > area.y + area.height) {
y_diff = (rect->y + rect->h) - (area.y + area.height);
}
rect->w -= x_diff;
rect->h -= y_diff;
}
void render_texture_asset(AssetID id, float x, float y, float w, float h) {
if (id < 0) {
return;
@@ -529,14 +608,6 @@ void set_playing_music_volume(int8_t volume) { Mix_VolumeMusic(volume); }
bool is_music_playing() { return Mix_PlayingMusic(); }
void close_asset(AssetID *id) {
if (*id < 0) {
return;
}
remove_asset_from_manager(GameEngine.asset_manager, id);
}
void handle_events() {
if (!(GameEngine.running)) {
return;
@@ -586,31 +657,3 @@ void render() {
swap_render_buffers(&(GameEngine.window));
}
void close_engine() {
destroy_asset_manager(&(GameEngine.asset_manager));
destroy_object_store(&(GameEngine.object_store));
destroy_window(&(GameEngine.window));
GameEngine.last_time = 0.0f;
GameEngine.delta = 0.0f;
GameEngine.running = false;
Mix_CloseAudio();
Mix_Quit();
TTF_Quit();
IMG_Quit();
SDL_Quit();
#ifdef DEBUG_ENGINE
printf("Game closed successfully\n");
#endif /* DEBUG_ENGINE */
}
+18 -10
View File
@@ -41,9 +41,22 @@ ObjectStore *create_object_store() {
return os;
}
bool object_exists(ObjectStore *os, void *object) {
// This is a crude implementation for checking that an object has already
// been added.
// TODO (Abdelrahman): Figure out if there is a better way of doing this
for (size_t i = 0; i < MAX_OBJ_COUNT; ++i) {
if (object == os->object_ptrs[i]) {
return true;
}
}
return false;
}
ObjectID add_object_to_store(ObjectStore *os, void *object, EventHandlerPtr eh,
UpdateFuncPtr uf, RenderFuncPtr rf) {
if (!os || !object ||
if (!os || !object || object_exists(os, object) ||
(os->count + 1 == MAX_OBJ_COUNT && os->deleted_count == 0)) {
#ifdef DEBUG_ENGINE
printf("Failed to add object\n");
@@ -52,14 +65,6 @@ ObjectID add_object_to_store(ObjectStore *os, void *object, EventHandlerPtr eh,
return INVALID_ID;
}
// TODO (Abdelrahman): Need to figure out a way to check that the object
// hasn't already been added to the store. If it has been added before, the
// function should return -1 indicating an error to ensure that there aren't
// two references to the same object without the user being aware that they
// refer to the same thing. If the user intentionally copies the ID of one
// object, then it would be their responsibility to make sure they don't
// delete the object using one of the IDs, then try to use it with the other.
ObjectID id;
if (os->deleted_count > 0) {
@@ -141,7 +146,10 @@ void destroy_object_store(ObjectStore **os) {
return;
}
for (size_t i = 0; i < (*os)->count; ++i) {
// TODO (Abdelrahman): Figure out if there is a better way to remove all of
// the existing objects without having to loop for the maximum number of
// objects allowed.
for (size_t i = 0; i < MAX_OBJ_COUNT; ++i) {
(*os)->object_ptrs[i] = NULL;
(*os)->event_handlers[i] = NULL;
(*os)->update_funcs[i] = NULL;
+20 -11
View File
@@ -15,24 +15,24 @@ struct renderer {
SDL_Renderer *SDL_base_renderer;
};
Renderer *create_renderer() { return (Renderer *)malloc(sizeof(Renderer)); }
bool init_renderer(Renderer *renderer, SDL_Window *window) {
if (!renderer || !window) {
return false;
Renderer *create_renderer(SDL_Window *window) {
Renderer *renderer = (Renderer *)malloc(sizeof(Renderer));
if (!renderer) {
return NULL;
}
renderer->SDL_base_renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!(renderer->SDL_base_renderer)) {
destroy_renderer(&renderer);
// Technically speaking, I believe it should be possible to just return
// the pointer. However, to make sure that everything is clear, I am
// performing a test that returns true or false based on whether the
// pointer was initialised or not.
return renderer->SDL_base_renderer ? true : false;
return NULL;
}
return renderer;
}
void get_drawable_size(Renderer *renderer, int32_t *w, int32_t *h) {
void get_render_output_size(Renderer *renderer, int32_t *w, int32_t *h) {
SDL_GetRendererOutputSize(renderer->SDL_base_renderer, w, h);
}
@@ -47,6 +47,15 @@ void render_clear(const Renderer *renderer, SDL_Color color) {
SDL_RenderClear(renderer->SDL_base_renderer);
}
void fill_rect(const Renderer *renderer, SDL_Color color, float x, float y,
float w, float h) {
SDL_FRect dest = {x, y, w, h};
SDL_SetRenderDrawColor(renderer->SDL_base_renderer, color.r, color.g, color.b,
color.a);
SDL_RenderFillRectF(renderer->SDL_base_renderer, &dest);
}
void render_surface(const Renderer *renderer, SDL_Surface *surface, float x,
float y, float w, float h) {
SDL_Texture *texture =
+63 -8
View File
@@ -4,8 +4,10 @@
#include "engine_build_config.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#ifdef DEBUG_ENGINE
#include <stdio.h>
#endif /* DEBUG_ENGINE */
bool init_window(Window *wnd, const char *title, WindowSize size,
@@ -32,22 +34,62 @@ bool init_window(Window *wnd, const char *title, WindowSize size,
wnd->defined_size.height = size.height;
wnd->background_color = color;
wnd->full_screen = full_screen;
wnd->renderer = create_renderer();
wnd->renderer = create_renderer(wnd->SDL_base_window);
if (!wnd->renderer || !init_renderer(wnd->renderer, wnd->SDL_base_window)) {
if (!wnd->renderer) {
destroy_window(wnd);
return false;
}
get_drawable_size(wnd->renderer, &(wnd->drawable_size.width),
&(wnd->drawable_size.height));
get_render_output_size(wnd->renderer, &(wnd->real_size.width),
&(wnd->real_size.height));
wnd->drawing_area.x = 0;
wnd->drawing_area.y = 0;
wnd->drawing_area.width = wnd->real_size.width;
wnd->drawing_area.height = wnd->real_size.height;
if (full_screen) {
// Fit the window into the screen when running in full screen mode on
// a non-mobile platform. Mobile platforms will be handled differently
float window_size_ratio =
(float)wnd->defined_size.width / (float)wnd->defined_size.height;
// Fit the drawing area size to the smallest dimension of the screen and
// make sure the drawing area is centered
if (wnd->real_size.height <= wnd->real_size.width) {
wnd->drawing_area.width =
(int32_t)(window_size_ratio * wnd->real_size.height);
wnd->drawing_area.x =
0.0f + ((wnd->real_size.width - wnd->drawing_area.width) / 2.0f);
} else {
wnd->drawing_area.height =
(int32_t)((float)wnd->real_size.width / window_size_ratio);
wnd->drawing_area.y =
0.0f + ((wnd->real_size.height - wnd->drawing_area.height) / 2.0f);
}
// Update the defined size, because in full screen mode, it would be equal
// to the screen dimensions
SDL_DisplayMode mode;
if (SDL_GetWindowDisplayMode(wnd->SDL_base_window, &mode) != 0) {
destroy_window(wnd);
return false;
}
wnd->defined_size.width = mode.w;
wnd->defined_size.height = mode.h;
}
#ifdef DEBUG_ENGINE
printf("Renderer initialised successfully\n");
printf("Window initialised successfully with width %d and height %d\n",
wnd->drawable_size.width, wnd->drawable_size.height);
wnd->real_size.width, wnd->real_size.height);
#endif /* DEBUG_ENGINE */
return true;
@@ -58,7 +100,18 @@ void clear_window(const Window *wnd) {
return;
}
render_clear(wnd->renderer, wnd->background_color);
if (wnd->full_screen) {
// When rendering in full screen on a non-mobile platform, we fill the
// entire screen with black, then fill an area that fits the window
// dimensions with the user-defined backgroud color of the window
render_clear(wnd->renderer, (SDL_Color){0, 0, 0, 255});
fill_rect(wnd->renderer, wnd->background_color, wnd->drawing_area.x,
wnd->drawing_area.y, wnd->drawing_area.width,
wnd->drawing_area.height);
} else {
render_clear(wnd->renderer, wnd->background_color);
}
}
void swap_render_buffers(const Window *wnd) {
@@ -82,8 +135,10 @@ void destroy_window(Window *wnd) {
SDL_DestroyWindow(wnd->SDL_base_window);
wnd->SDL_base_window = NULL;
wnd->full_screen = false;
wnd->defined_size = (WindowSize){0, 0};
wnd->drawable_size = (WindowSize){0, 0};
wnd->real_size = (WindowSize){0, 0};
wnd->drawing_area = (WindowDrawingArea){0.0f, 0.0f, 0.0f, 0.0f};
wnd->background_color = (SDL_Color){0, 0, 0, 255};
#ifdef DEBUG_ENGINE