From e3e3d5f4768d299130536361439b59ee79b15537 Mon Sep 17 00:00:00 2001 From: Ulysse Cura Date: Thu, 24 Sep 2026 22:28:38 +0200 Subject: [PATCH] Sarting to add windows support on cygwin. --- asset_builder/Makefile | 4 +- assets/Makefile | 4 +- configure | 38 +++++- core/Makefile | 4 +- core/host/windows/Makefile | 5 + core/host/windows/display.c | 193 +++++++++++++++++++++++++++++++ core/host/windows/display.h | 33 ++++++ core/host/windows/memory_alloc.h | 6 + core/host/windows/texture.c | 67 +++++++++++ core/host/windows/texture.h | 16 +++ core/host/windows/vector2d.c | 19 +++ core/host/windows/vector2d.h | 153 ++++++++++++++++++++++++ src/Makefile | 4 +- 13 files changed, 535 insertions(+), 11 deletions(-) create mode 100644 core/host/windows/Makefile create mode 100644 core/host/windows/display.c create mode 100644 core/host/windows/display.h create mode 100644 core/host/windows/memory_alloc.h create mode 100644 core/host/windows/texture.c create mode 100644 core/host/windows/texture.h create mode 100644 core/host/windows/vector2d.c create mode 100644 core/host/windows/vector2d.h diff --git a/asset_builder/Makefile b/asset_builder/Makefile index 62beb15..945da6f 100644 --- a/asset_builder/Makefile +++ b/asset_builder/Makefile @@ -17,14 +17,14 @@ endif $(ASSET_BUILDER_OUTPUT): $(OBJECTS) $(Q)mkdir -p $(dir $@) + @echo $(ECHO_OPTION) "[100%] $(YELLOW)Linking $(notdir $(ASSET_BUILDER_OUTPUT))$(RESET)" $(Q)$(HOST_CC) $(ASSET_BUILDER_CFLAGS) $(OBJECTS) -o $@ $(ASSET_BUILDER_LDFLAGS) - @echo "[100%] $(YELLOW)Linking $(notdir $(ASSET_BUILDER_OUTPUT))$(RESET)" $(BUILD_DIR)/$(notdir $(ASSET_BUILDER_DIR))/%.o: $(ASSET_BUILDER_DIR)/% $(Q)mkdir -p $(dir $@) $(eval CURRENT_FILE := $(shell echo $$(($(CURRENT_FILE)+1)))) $(eval PERCENTAGE := $(shell echo $$(($(CURRENT_FILE)*100/$(TOTAL_BUILD_FILES))))) + @echo $(ECHO_OPTION) "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)" $(Q)$(HOST_CC) $(ASSET_BUILDER_CFLAGS) $(INCLUDES:%=-I%) -MMD -MP -c $< -o $@ - @echo "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)" -include $(ASSET_BUILDER_OBJECTS:.o=.d) diff --git a/assets/Makefile b/assets/Makefile index e5a27e8..051e61f 100644 --- a/assets/Makefile +++ b/assets/Makefile @@ -19,14 +19,14 @@ TOTAL_BUILD_FILES := $(shell echo $$(($$($(MAKE) -n $(OBJECTS) TOTAL_BUILD_FILES endif $(ASSET_OUTPUT): $(OBJECTS) $(ASSET_BUILDER_OUTPUT) - @echo "[100%] $(YELLOW)Building $(notdir $(ASSET_OUTPUT))$(RESET)" + @echo $(ECHO_OPTION) "[100%] $(YELLOW)Building $(notdir $(ASSET_OUTPUT))$(RESET)" $(Q)$(ASSET_BUILDER_OUTPUT) $@ $(OBJECTS) $(BUILD_DIR)/$(notdir $(ASSETS_DIR))/%.o: $(ASSETS_DIR)/% $(ASSET_BUILDER_OUTPUT) $(Q)mkdir -p $(dir $@) $(eval CURRENT_FILE := $(shell echo $$(($(CURRENT_FILE) + 1)))) $(eval PERCENTAGE := $(shell echo $$(($(CURRENT_FILE) * 100 / $(TOTAL_BUILD_FILES))))) - @echo "[$(PERCENTAGE)%] $(GREEN)Building ASSET object $@$(RESET)" + @echo $(ECHO_OPTION) "[$(PERCENTAGE)%] $(GREEN)Building ASSET object $@$(RESET)" $(Q)$(ASSET_BUILDER_OUTPUT) $@ -c $< $(ASSET_BUILDER_OUTPUT): FORCE diff --git a/configure b/configure index 752a40e..d6c19c4 100755 --- a/configure +++ b/configure @@ -91,9 +91,9 @@ if [ "${CURRENT_HOST}" = "guess" ]; then linux*) CURRENT_HOST="linux" ;; -# msys*|cygwin*) -# CURRENT_HOST="windows" -# ;; + msys*|cygwin*) + CURRENT_HOST="windows" + ;; # darwin*) # CURRENT_HOST="macos" # ;; @@ -116,6 +116,12 @@ case "${CURRENT_HOST}" in ASSET_BUILDER_CFLAGS="--std=c17 -O2 \$(shell pkg-config --cflags libpng json-c)" ASSET_BUILDER_LDFLAGS="\$(shell pkg-config --libs libpng json-c)" ;; + windows) + HOST_CC="x86_64-w64-mingw32-gcc" + HOST_AR="x86_64-w64-mingw32-ar" + ASSET_BUILDER_CFLAGS="--std=c17 -O2 \$(shell x86_64-w64-mingw32-pkg-config --cflags libpng json-c)" + ASSET_BUILDER_LDFLAGS="\$(shell x86_64-w64-mingw32-pkg-config --libs libpng json-c)" + ;; *) print_error "Compiling from ${CURRENT_HOST} is not supported yet." ;; @@ -139,6 +145,24 @@ case "${TARGET_HOST}" in esac ;; + windows) + echo "Configuring for target: ${TARGET_HOST}" + + case "${CURRENT_HOST}" in + windows) + TARGET_HOST_CC="x86_64-w64-mingw32-gcc" + TARGET_HOST_AR="x86_64-w64-mingw32-ar" + TARGET_HOST_CFLAGS="--std=c17 -Os -DASSET_FILE_NAME=\\\"\$(notdir \$(ASSET_OUTPUT))\\\" -D_GNU_SOURCE=1 \$(shell x86_64-w64-mingw32-pkg-config --cflags sdl3 lua)" + TARGET_HOST_LDFLAGS="\$(shell x86_64-w64-mingw32-pkg-config --libs sdl3 lua) -lm" + TARGET_HOST_LIBCORE="libcore.dll" + ;; + + *) + print_error "Cross compiling from ${CURRENT_HOST} for ${TARGET_HOST} is not supported yet." + ;; + esac + ;; + fxcg50) echo "Configuring for target: ${TARGET_HOST}" @@ -181,6 +205,14 @@ ASSET_BUILDER_CFLAGS = ${ASSET_BUILDER_CFLAGS} ASSET_BUILDER_LDFLAGS = ${ASSET_BUILDER_LDFLAGS} EOF +if [ "${CURRENT_HOST}" = "windows" ]; then +tee -a "${CONFIG_FILE}" < +#include +#include "asset_manager.h" +#include "game.h" +#include "texture.h" +#include "errors.h" + +inline int display_init(void) +{ + if(!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS)) + { + error_printf("Failed to initialise SDL3 : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + if(!SDL_SetAppMetadata("2D_Engine_SDL3_Core", "1.0", "2D_Engine")) + { + error_printf("Failed to set app metadata : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + game.display.window = SDL_CreateWindow("2D_Engine", DISPLAY_WIDTH, DISPLAY_HEIGHT, 0); + if(!game.display.window) + { + error_printf("Failed to create window : %s", SDL_GetError()); + return EXIT_FAILURE; + } + +#ifdef DRIVERS + debug_printf("Listing available render drivers."); + const int NB_DRIVERS = SDL_GetNumRenderDrivers(); + for(int i = 0; i < NB_DRIVERS; i++) + debug_printf("%d : %s", i, SDL_GetRenderDriver(i)); + + warning_printf("This build of 2D_Engine_C only support the following drivers : %s", DRIVERS); + + game.display.renderer = SDL_CreateRenderer(game.display.window, DRIVERS); +#else + game.display.renderer = SDL_CreateRenderer(game.display.window, NULL); +#endif // DRIVERS + + if(!game.display.renderer) + { + error_printf("Failed to create renderer : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + if(!SDL_SetRenderVSync(game.display.renderer, SDL_RENDERER_VSYNC_ADAPTIVE)) + { + error_printf("Failed to activate adaptative vsync : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + if(!SDL_SetRenderDrawBlendMode(game.display.renderer, SDL_BLENDMODE_BLEND)) + { + error_printf("Failed to set renderer draw blend mode : %s", SDL_GetError()); + return EXIT_FAILURE; + } +/* + if(!SDL_SetDefaultTextureScaleMode(game.display.renderer, SDL_SCALEMODE_NEAREST)) + { + error_printf("Failed to set default texture scale mode : %s", SDL_GetError()); + return EXIT_FAILURE; + } +*/ + debug_printf("Initialized display."); + + return EXIT_SUCCESS; +} + +inline void display_exit(void) +{ + if(game.display.renderer) SDL_DestroyRenderer(game.display.renderer); + if(game.display.window) SDL_DestroyWindow(game.display.window); + SDL_Quit(); + + debug_printf("Exited display."); +} + +inline int display_clear(void) +{ + if(!SDL_SetRenderDrawColor(game.display.renderer, C_WHITE, 255)) + { + error_printf("Failed to change renderer draw color : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + if(!SDL_RenderClear(game.display.renderer)) + { + error_printf("Failed to clear renderer : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +inline int display_update(void) +{ + if(!SDL_RenderPresent(game.display.renderer)) + { + error_printf("Failed to present renderer : %s", SDL_GetError()); + return EXIT_FAILURE; + } + +#if DEBUG >= 1 + float fps = 1000.0f / game.delta_time_ms; + static float max_fps_10s = 0.0f; + static float low_fps_10s = 10000000.0f; + + if(fps > max_fps_10s) max_fps_10s = fps; + if(fps < low_fps_10s) low_fps_10s = fps; + + static float fps_update_delta_time = 0.0f; + fps_update_delta_time += game.delta_time_ms; + + if(fps_update_delta_time >= 100.0f) + { + fps_update_delta_time = 0.0f; + char title[100]; + if(sprintf(title, "2D_Engine | cur: %.2ffps | max: %.2ffps | low: %.2ffps", fps, max_fps_10s, low_fps_10s) == -1) + { + error_printf("Sprintf failed : %d", errno); + return EXIT_FAILURE; + } + if(!SDL_SetWindowTitle(game.display.window, title)) + { + error_printf("Failed to set window title : %s", SDL_GetError()); + return EXIT_FAILURE; + } + } + + static float fps_max_low_update_delta_time = 0.0f; + fps_max_low_update_delta_time += game.delta_time_ms; + + if(fps_max_low_update_delta_time > 10000.0f) + { + fps_max_low_update_delta_time = 0.0f; + max_fps_10s = 0.0f; + low_fps_10s = 10000000.0f; + } +#endif // DEBUG >= 1 + + return EXIT_SUCCESS; +} + +int display_draw_texture(texture_t *texture, const rect_t *src_rect, const rect_t *dst_rect, bool flip) +{ + frect_t src_frect; + if(src_rect) rect_to_frect(src_rect, &src_frect); + + frect_t dst_frect; + if(dst_rect) + dst_frect = (frect_t) { + .x = (dst_rect->x - game.camera.view_rect.x) * (DISPLAY_WIDTH / game.camera.view_rect.w), + .y = (dst_rect->y - game.camera.view_rect.y) * (DISPLAY_HEIGHT / game.camera.view_rect.h), + .w = dst_rect->w * (DISPLAY_WIDTH / game.camera.view_rect.w), + .h = dst_rect->h * (DISPLAY_HEIGHT / game.camera.view_rect.h) + }; + + if(!SDL_RenderTextureRotated(game.display.renderer, texture, src_rect ? &src_frect : NULL, dst_rect ? &dst_frect : NULL, 0, NULL, flip)) + { + error_printf("Failed to copy texture to renderer : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +int display_draw_frect(const frect_t *frect, unsigned int r, unsigned int g, unsigned int b, unsigned int a) +{ + if(!SDL_SetRenderDrawColor(game.display.renderer, r, g, b, a)) + { + error_printf("Failed to change renderer draw color : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + frect_t display_frect = { + .x = (frect->x - game.camera.view_rect.x) * (DISPLAY_WIDTH / game.camera.view_rect.w), + .y = (frect->y - game.camera.view_rect.y) * (DISPLAY_HEIGHT / game.camera.view_rect.h), + .w = frect->w * (DISPLAY_WIDTH / game.camera.view_rect.w), + .h = frect->h * (DISPLAY_HEIGHT / game.camera.view_rect.h) + }; + + if(!SDL_RenderRect(game.display.renderer, &display_frect)) + { + error_printf("Failed to draw rectangle to renderer : %s", SDL_GetError()); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/core/host/windows/display.h b/core/host/windows/display.h new file mode 100644 index 0000000..3083c53 --- /dev/null +++ b/core/host/windows/display.h @@ -0,0 +1,33 @@ +#ifndef DISPLAY_H +#define DISPLAY_H + +#include +#include "texture.h" +#include "vector2d.h" + +#define C_BLACK 0, 0, 0 +#define C_RED 255, 0, 0 +#define C_GREEN 0, 255, 0 +#define C_BLUE 0, 0, 255 +#define C_WHITE 255, 255, 255 + +#define DISPLAY_WIDTH 720 +#define DISPLAY_HEIGHT 480 + +#define DRIVERS "vulkan,gpu,software" + +typedef struct display_t { + SDL_Window *window; + SDL_Renderer *renderer; +} display_t; + +int display_init(void); +void display_exit(void); + +int display_clear(void); +int display_update(void); + +int display_draw_texture(texture_t *texture, const rect_t *src_rect, const rect_t *dst_rect, bool flip); +int display_draw_frect(const frect_t *frect, unsigned int r, unsigned int g, unsigned int b, unsigned int a); + +#endif // DISPLAY_H \ No newline at end of file diff --git a/core/host/windows/memory_alloc.h b/core/host/windows/memory_alloc.h new file mode 100644 index 0000000..ef8e8b6 --- /dev/null +++ b/core/host/windows/memory_alloc.h @@ -0,0 +1,6 @@ +#ifndef MEMORY_ALLOC_H +#define MEMORY_ALLOC_H + +#include + +#endif // MEMORY_ALLOC_H \ No newline at end of file diff --git a/core/host/windows/texture.c b/core/host/windows/texture.c new file mode 100644 index 0000000..b159a19 --- /dev/null +++ b/core/host/windows/texture.c @@ -0,0 +1,67 @@ +#include "texture.h" + +#include "asset_manager.h" +#include "game.h" +#include "display.h" +#include "errors.h" + +texture_t *load_texture(void) +{ + uint16_t width, height; + + if(fread(&width, sizeof(width), 1, game.asset_manager.asset_file) != 1) + { + error_printf("Failed to read in asset file."); + return NULL; + } + + if(fread(&height, sizeof(height), 1, game.asset_manager.asset_file) != 1) + { + error_printf("Failed to read in asset file."); + return NULL; + } + + SDL_Surface *tmp_surface = SDL_CreateSurface(width, height, SDL_PIXELFORMAT_RGB565); + if(!tmp_surface) + { + error_printf("Failed to create temporary surface : %s", SDL_GetError()); + return NULL; + } + + if(!SDL_LockSurface(tmp_surface)) + { + error_printf("Failed to lock temporary surface : %s", SDL_GetError()); + goto_failure; + } + + if(fread(tmp_surface->pixels, sizeof(uint16_t), width * height, game.asset_manager.asset_file) != (size_t)width * (size_t)height) + { + error_printf("Failed to read asset file."); + goto_failure; + } + + if(!SDL_SetSurfaceColorKey(tmp_surface, true, TEXTURE_TRANSPARENT_COLOR)) + { + error_printf("Failed to set color key of temporary surface : %s", SDL_GetError()); + goto_failure; + } + + texture_t *texture = SDL_CreateTextureFromSurface(game.display.renderer, tmp_surface); + if(!texture) + { + error_printf("Failed to create texture from temporary surface : %s", SDL_GetError()); + goto_failure; + } + + SDL_DestroySurface(tmp_surface); + return texture; + +failure: + SDL_DestroySurface(tmp_surface); + return NULL; +} + +void destroy_texture(texture_t *texture) +{ + SDL_DestroyTexture(texture); +} diff --git a/core/host/windows/texture.h b/core/host/windows/texture.h new file mode 100644 index 0000000..b61aabf --- /dev/null +++ b/core/host/windows/texture.h @@ -0,0 +1,16 @@ +#ifndef TEXTURE_H +#define TEXTURE_H + +#include + +#define TEXTURE_TRANSPARENT_COLOR 0x0001 + +typedef SDL_Texture texture_t; + +#define get_width(T) T->w +#define get_height(T) T->h + +texture_t *load_texture(void); +void destroy_texture(texture_t *texture); + +#endif // TEXTURE_H \ No newline at end of file diff --git a/core/host/windows/vector2d.c b/core/host/windows/vector2d.c new file mode 100644 index 0000000..928b5f6 --- /dev/null +++ b/core/host/windows/vector2d.c @@ -0,0 +1,19 @@ +#include "vector2d.h" + +void frect_to_rect(const frect_t *frect, rect_t *rect) +{ + rect->x = (int)frect->x; + rect->y = (int)frect->y; + rect->w = (int)frect->w; + rect->h = (int)frect->h; +} + +inline float lerp(float a, float b, float s) +{ + return a * (1 - s) + b * s; +} + +inline float lerp_time(float a, float b, float s, float t) +{ + return lerp(a, b, 1 - powf(1 - s, t * 60.0f)); +} \ No newline at end of file diff --git a/core/host/windows/vector2d.h b/core/host/windows/vector2d.h new file mode 100644 index 0000000..742f469 --- /dev/null +++ b/core/host/windows/vector2d.h @@ -0,0 +1,153 @@ +#ifndef VECTOR2D_H +#define VECTOR2D_H + +#include +#include + +/** + * @brief Point struct. (int) + * + * @param x X pos + * @param y Y pos +*/ +typedef SDL_Point vector2d_t; + +/** + * @brief Point struct. (float) + * + * @param x X pos + * @param y Y pos +*/ +typedef SDL_FPoint fvector2d_t; + +/** + * @brief Rectangle struct. (int) + * + * @param x X pos + * @param y Y pos + * @param w Width + * @param h Height +*/ +typedef SDL_Rect rect_t; + +/** + * @brief Rectangle struct. (float) + * + * @param x X pos + * @param y Y pos + * @param w Width + * @param h Height +*/ +typedef SDL_FRect frect_t; + +#define EPSILON 0.0f + +#define f_is_zero(X) (fabsf(X) <= EPSILON) + +#define f_is_not_zero(X) (fabsf(X) > EPSILON) + +#define rect_to_frect(R, FR) SDL_RectToFRect(R, FR) + +void frect_to_rect(const frect_t *frect, rect_t *rect); + +/** + * @brief Verify if a point is in a rectangle. (int) + * + * @param P Vector2d + * @param R Rectangle + * + * @return True if the point is in the rectangle, else false. +*/ +#define point_in_rect(P, R) SDL_PointInRect(P, R) + +/** + * @brief Verify if a point is in a rectangle. (float) + * + * @param P Vector2d + * @param R Rectangle + * + * @return True if the point is in the rectangle, else false. +*/ +#define fpoint_in_frect(P, R) SDL_PointInRectF(P, R) + +/** + * @brief Verify if a rectangle is empty + * + * @param R Rectangle + * + * @return True if the rectangle is empty, else false. +*/ +#define rect_empty(R) SDL_RectEmpty(R) + +/** + * @brief Verify if a rectangle is empty + * + * @param R Rectangle + * + * @return True if the rectangle is empty, else false. +*/ +#define frect_empty(R) SDL_FRectEmpty(R) + +/** + * @brief Verify if there is a intersction between two rectangles. (int) + * + * @param A Rectangle A + * @param B Rectangle B + * + * @return True if there is an intersection, else false. +*/ +#define has_intersection(A, B) SDL_HasRectIntersection(A, B) + +/** + * @brief Verify if there is a intersction between two rectangles. (float) + * + * @param A Rectangle A + * @param B Rectangle B + * + * @return True if there is an intersection, else false. +*/ +#define fhas_intersection(A, B) SDL_HasRectIntersectionFloat(A, B) + +/** + * @brief Verify if there is an intersection between two rectangles and get the intersection rectangle. (int) + * + * @param A Rectangle A + * @param B Rectangle B + * @param result The intersection rectangle + * + * @return True if there is an intersection, else false. +*/ +#define intersect_rect(A, B, result) SDL_GetRectIntersection(A, B, result) + +/** + * @brief Verify if there is an intersection between two rectangles and get the intersection rectangle. (float) + * + * @param A Rectangle A + * @param B Rectangle B + * @param result The intersection rectangle + * + * @return True if there is an intersection, else false. +*/ +#define intersect_frect(A, B, result) SDL_GetRectIntersectionFloat(A, B, result) + +/** + * @brief Lerp a to b with smoothing factor s. + * + * @param a Current value + * @param b Target value + * @param s Smoothing factor + */ +float lerp(float a, float b, float s); + +/** + * @brief Lerp a to b with smoothing factor s and time basis t. + * Smoothing factor is for 60 fps call time. + * + * @param a Current value + * @param b Target value + * @param s Smoothing factor + * @param t Delta time + */ +float lerp_time(float a, float b, float s, float t); + +#endif // VECTOR2D_H \ No newline at end of file diff --git a/src/Makefile b/src/Makefile index 4ff26f5..247433b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,14 +23,14 @@ TOTAL_BUILD_FILES := $(shell echo $$(($$($(MAKE) -n $(OBJECTS) TOTAL_BUILD_FILES endif $(OUTPUT): $(OBJECTS) $(LIBCORE) - @echo "[100%] $(YELLOW)Linking $(notdir $(OUTPUT))$(RESET)" + @echo $(ECHO_OPTION) "[100%] $(YELLOW)Linking $(notdir $(OUTPUT))$(RESET)" $(Q)$(TARGET_HOST_CC) $(TARGET_HOST_CFLAGS) $(OBJECTS) -o $@ -L$(BUILD_DIR) -lcore $(TARGET_HOST_LDFLAGS) $(BUILD_DIR)/$(notdir $(SOURCE_DIR))/%.o: $(SOURCE_DIR)/% $(Q)mkdir -p $(dir $@) $(eval CURRENT_FILE := $(shell echo $$(($(CURRENT_FILE) + 1)))) $(eval PERCENTAGE := $(shell echo $$(($(CURRENT_FILE) * 100 / $(TOTAL_BUILD_FILES))))) - @echo "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)" + @echo $(ECHO_OPTION) "[$(PERCENTAGE)%] $(GREEN)Building C object $@$(RESET)" $(Q)$(TARGET_HOST_CC) $(TARGET_HOST_CFLAGS) $(INCLUDES:%=-I%) -MMD -MP -c $< -o $@ $(LIBCORE): FORCE