Author: mesyeti <mesyeti@mesyeti.uk>
compiles
.gitignore | 2 .gitmodules | 3 LICENSE | 21 ++ Makefile | 89 +++++++++ source/backend.c | 21 ++ source/backend.h | 65 +++++++ source/backends/glLegacy.c | 357 ++++++++++++++++++++++++++++++++++++++++ source/backends/glLegacy.h | 34 +++ source/backends/stub.c | 143 ++++++++++++++++ source/backends/stub.h | 8 source/common.h | 23 ++ source/event.c | 96 ++++++++++ source/event.h | 81 +++++++++ source/event/sdl2.c | 109 ++++++++++++ source/input.c | 127 ++++++++++++++ source/input.h | 65 +++++++ source/input/sdl.c | 222 ++++++++++++++++++++++++ source/input/sdl.h | 9 + source/keyboard.c | 118 +++++++++++++ source/keyboard.h | 118 +++++++++++++ source/log.c | 121 +++++++++++++ source/log.h | 10 + source/main.c | 3 source/mem.c | 27 +++ source/mem.h | 11 + source/platform.h | 10 + source/platform/unix.c | 20 ++ source/platform/windows.c | 46 +++++ source/stb.c | 7 source/stb.h | 12 + source/texture.c | 75 ++++++++ source/texture.h | 9 + source/types.c | 7 source/types.h | 34 +++ source/util.c | 197 ++++++++++++++++++++++ source/util.h | 48 +++++ source/video.c | 71 +++++++ source/video.h | 27 +++ source/window.h | 29 +++ source/window/sdl2.c | 72 ++++++++ source/window/sdl2.h | 12 +
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..0a56c13a91b347267c67f8d1e7503768586478ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/yterm diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..431d35d81572daa170007f517078c1565db1310b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/stb"] + path = lib/stb + url = https://github.com/nothings/stb diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..1ed3e4dda99de0370f14cca4125e048a128b66aa --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 MESYETI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b89644fb3d2fbe3fefc9f998df99f69022c19bd7 --- /dev/null +++ b/Makefile @@ -0,0 +1,89 @@ +SOURCES := $(wildcard source/*.c) $(wildcard source/**/*.c) +OBJECTS := $(patsubst source/%.c,bin/%.o,$(SOURCES)) +PLAT := $(shell uname -s) + +ifeq ($(PLAT),NetBSD) + override CPPFLAGS += -I/usr/X11R7/include -I/usr/pkg/include + override LDFLAGS += -L/usr/X11R7/lib -L/usr/pkg/lib -Wl,-R/usr/X11R7/lib -Wl,-R/usr/pkg/lib +endif + +ifeq ($(PLAT),windows) + CC := x86_64-w64-mingw32-gcc + override LDLIBS += -lkernel32 -l:libSDL2.a -lole32 -loleaut32 -limm32 + override LDLIBS += -lsetupapi -lversion -lgdi32 -lwinmm -lopengl32 +else + override LDLIBS += -lSDL2 -lGL +endif +LD := $(CC) + +OUT := yterm + +ifeq ($(STATIC), y) + override CFLAGS += -static -static-libgcc -static-libstdc++ +endif + +override CFLAGS += -std=c99 -Wall -Wextra -Wuninitialized -Wundef -pedantic -Ilib -Werror=return-type +override LDLIBS += -lm + +override CPPFLAGS += -DYT_BACKEND_GL_11 +override CPPFLAGS += -DYT_WINDOW_SDL2 -DYT_INPUT_SDL2 -DYT_EVENT_SDL2 +override CPPFLAGS += -DSDL_MAIN_HANDLED -D_POSIX_C_SOURCE=199309L + +ifeq ($(BUILD),release) + override CFLAGS += -O3 + #override CPPFLAGS += -NDEBUG +else + override CFLAGS += -O0 -ggdb -fno-omit-frame-pointer + override LDFLAGS += -O0 -ggdb -fno-omit-frame-pointer + ifeq ($(ASAN),y) + override CFLAGS += -fsanitize=address,undefined + override LDFLAGS += -fsanitize=address,undefined + endif +endif + +.SECONDEXPANSION: + +deps.filter := %.c %.h +deps.option := -MM +define deps +$$(filter $$(deps.filter),,$$(shell $(CC) $(CFLAGS) $(CPPFLAGS) -E $(deps.option) $(1))) +endef + +all: $(OUT) + @: + +run: $(OUT) + '$(dir $<)$(notdir $<)' $(RUNFLAGS) + +yterm: $(OBJECTS) + $(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@ + +bin/: + mkdir -p bin + +bin/backends: + mkdir -p bin/backends + + +bin/input: + mkdir -p bin/input + +bin/window: + mkdir -p bin/window + +bin/event: + mkdir -p bin/event + +bin/platform: + mkdir -p bin/platform + +bin/%.o: source/%.c $(call deps,source/%.c) | bin/ bin/backends bin/input bin/window bin/event bin/platform + $(CC) $(CFLAGS) $(CPPFLAGS) $< -c -o $@ + +clean: + -rm -r bin + +distclean: clean + rm $(OUT) + +.PHONY: all run clean distclean install diff --git a/source/backend.c b/source/backend.c new file mode 100644 index 0000000000000000000000000000000000000000..dc71c0b9a001274147f77bcf3656a61eed1c1f0f --- /dev/null +++ b/source/backend.c @@ -0,0 +1,21 @@ +#include "backend.h" + +BackendOptions backendOptions = { + .vsync = true, + .name = "gl1" +}; + +void Backend_VLine(int x, int y, int thick, int len, Colour colour) { + Backend_RenderRect((Rect) {x, y, thick, len}, colour); +} + +void Backend_HLine(int x, int y, int thick, int len, Colour colour) { + Backend_RenderRect((Rect) {x, y, len, thick}, colour); +} + +void Backend_RenderRectOL(Rect rect, Colour colour) { + Backend_HLine(rect.x, rect.y, 1, rect.w, colour); + Backend_VLine(rect.x, rect.y, 1, rect.h, colour); + Backend_HLine(rect.x, rect.y + rect.h - 1, 1, rect.w, colour); + Backend_VLine(rect.x + rect.w - 1, rect.y, 1, rect.h, colour); +} diff --git a/source/backend.h b/source/backend.h new file mode 100644 index 0000000000000000000000000000000000000000..20e886e2cafdfb920f3f74c5211678f0f799e2d9 --- /dev/null +++ b/source/backend.h @@ -0,0 +1,65 @@ +#ifndef YT_BACKEND_H +#define YT_BACKEND_H + +// defined: +// (struct) Texture +#if defined(YT_BACKEND_GL_11) + #include "backends/glLegacy.h" +#elif defined(YT_BACKEND_STUB) + #include "backends/stub.h" +#else + #error "No backend" +#endif + +#include "types.h" +#include "video.h" +#include "window.h" + +typedef struct { + Rect rect; + bool enabled; +} BackendViewport; + +typedef struct { + bool vsync; + char name[20]; +} BackendOptions; + +extern BackendOptions backendOptions; + +typedef struct { + bool doTint; + Colour tint; +} TextureRenderOpt; + +// implemented per backend +void Backend_Init(bool beforeWindow); +void Backend_Free(void); +void Backend_SetTarget(Window* window); +Texture* Backend_LoadTexture(uint8_t* data, int w, int h, int aW, int aH, int ch); +void Backend_FreeTexture(Texture* texture); +Vec2 Backend_GetTextureSize(Texture* texture); +void Backend_OnWindowResize(void); + +void Backend_DrawTexture( + Texture* texture, TextureRenderOpt* p_opt, Rect* p_src, Rect* p_dest +); + +void Backend_Begin(void); +void Backend_Clear(uint8_t r, uint8_t g, uint8_t b); +void Backend_SetViewport(int x, int y, int w, int h); +void Backend_EnableViewport(bool enable); +void Backend_RenderRect(Rect rect, Colour colour); +void Backend_RenderLine(Vec2 a, Vec2 b, Colour colour); +void Backend_EnableAlpha(bool enable); +void Backend_FinishRender(void); + +BackendViewport Backend_SaveViewport(void); +void Backend_RestoreViewport(BackendViewport viewport); + +// portable +void Backend_HLine(int x, int y, int thick, int len, Colour colour); +void Backend_VLine(int x, int y, int thick, int len, Colour colour); +void Backend_RenderRectOL(Rect rect, Colour colour); + +#endif diff --git a/source/backends/glLegacy.c b/source/backends/glLegacy.c new file mode 100644 index 0000000000000000000000000000000000000000..61db6d5ba459a25d6cd6117a407368d0cf585e62 --- /dev/null +++ b/source/backends/glLegacy.c @@ -0,0 +1,357 @@ +#include "../stb.h" +#include "../mem.h" +#include "../util.h" +#include "../video.h" +#include "../backend.h" + +#ifdef YT_BACKEND_GL_11 +#include "glLegacy.h" + +#ifndef GL_CLAMP_TO_EDGE_SGIS + #define GL_CLAMP_TO_EDGE_SGIS GL_CLAMP_TO_EDGE +#endif + +static void GL_Error(GLenum error, const char* file, int line) { + const char* errorStr; + switch (error) { + case GL_INVALID_ENUM: errorStr = "Invalid enum"; break; + case GL_INVALID_VALUE: errorStr = "Invalid value"; break; + case GL_INVALID_OPERATION: errorStr = "Invalid operation"; break; + case GL_STACK_OVERFLOW: errorStr = "Stack overflow"; break; + case GL_STACK_UNDERFLOW: errorStr = "Stack underflow"; break; + case GL_OUT_OF_MEMORY: errorStr = "Out of memory"; break; + default: errorStr = "???"; + } + + Error("%s:%d: OpenGL error: %s", file, line, errorStr); +} + +#define GL_CALL(CALL, FILE, LINE) do { \ + CALL; \ + GLenum error = glGetError(); \ + \ + if (error != GL_NO_ERROR) { \ + GL_Error(error, FILE, LINE); \ + } \ +} while(0) + +#define GL(CALL) GL_CALL(CALL, __FILE__, __LINE__) + +typedef struct { + bool edgeClamp; + bool multiTexture; +} Features; + +static Features features = { + .edgeClamp = false, + .multiTexture = false +}; + +typedef struct { + bool alpha; + + BackendViewport viewport; + + SDL_GLContext ctx; + Texture textures[32]; +} State; + +static State state; + +void Backend_Init(bool beforeWindow) { + if (beforeWindow) { + int major = 1; + int minor = 1; + + assert(SDL_GL_SetAttribute( + SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY + ) == 0); + assert(SDL_GL_SetAttribute( + SDL_GL_CONTEXT_MAJOR_VERSION, major + ) == 0); + assert(SDL_GL_SetAttribute( + SDL_GL_CONTEXT_MINOR_VERSION, minor + ) == 0); + //#if USE_KHR_DEBUG + // assert(SDL_GL_SetAttribute( + // SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG + // ) == 0); + //#endif + + state.viewport.enabled = false; + state.viewport.rect = (Rect) {0, 0, 0, 0}; + + return; + } + + state.ctx = SDL_GL_CreateContext(video.windows[0].window); + assert(SDL_GL_MakeCurrent(video.windows[0].window, state.ctx) == 0); + + Log("Backend info: GL Legacy"); + + Log("=================="); + Log("Vendor: %s", (const char*) glGetString(GL_VENDOR)); + Log("Renderer: %s", (const char*) glGetString(GL_RENDERER)); + Log("Version: %s", (const char*) glGetString(GL_VERSION)); + + const char* ext = glGetString(GL_EXTENSIONS); + + if (strstr(ext, "GL_ARB_multitexture") != NULL) { + Log("ARB multitexture extension available"); + features.multiTexture = true; + } + else { + Log("ARB multitexture extension not available"); + features.multiTexture = false; + } + + if (strstr(ext, "SGIS_texture_edge_clamp") != NULL) { + Log("SGIS texture edge clamp extension available"); + features.edgeClamp = true; + } + else { + Log("SGIS texture edge clamp extension not available"); + features.edgeClamp = false; + } + + Log("=================="); + + /*Log("Extensions:"); + printf(" "); + + const char* ext = (const char*) glGetString(GL_EXTENSIONS); + for (size_t i = 0; ext[i] != 0; ++ i) { + if (ext[i] == ' ') { + printf("\n "); + } + else { + putchar(ext[i]); + } + } + + printf("\r");*/ + + glEnable(GL_TEXTURE_2D); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void Backend_Free(void) { + SDL_GL_DeleteContext(state.ctx); + // Model_Free(&state.model); +} + +void Backend_SetTarget(Window* window) { + (void) window; // unimplemented + + static bool warning = false; + if (!warning) { + warning = true; + Warn("Backend_SetTarget unsupported"); + } +} + +Texture* Backend_LoadTexture(uint8_t* data, int w, int h, int aW, int aH, int ch) { + GLuint tex; + GL(glGenTextures(1, &tex)); + GL(glBindTexture(GL_TEXTURE_2D, tex)); + GL(glTexImage2D( + GL_TEXTURE_2D, 0, ch, aW, aH, 0, (ch == 3) ? GL_RGB : GL_RGBA, + GL_UNSIGNED_BYTE, data + )); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)); + GL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)); + + free(data); + GL(glBindTexture(GL_TEXTURE_2D, 0)); + + // now put this texture in the texture array + for (size_t i = 0; i < sizeof(state.textures) / sizeof(Texture); ++ i) { + if (!state.textures[i].used) { + state.textures[i].used = true; + state.textures[i].name = tex; + state.textures[i].width = w; + state.textures[i].height = h; + state.textures[i].actualWidth = aW; + state.textures[i].actualHeight = aH; + return &state.textures[i]; + } + } + + Error("No more room for textures"); + return NULL; +} + +void Backend_FreeTexture(Texture* texture) { + GL(glDeleteTextures(1, &texture->name)); + texture->used = false; +} + +Vec2 Backend_GetTextureSize(Texture* texture) { + return (Vec2) {texture->width, texture->height}; +} + +void Backend_DrawTexture( + Texture* texture, TextureRenderOpt* p_opt, Rect* p_src, Rect* p_dest +) { + TextureRenderOpt opt; + if (p_opt == NULL) { + opt.doTint = false; + } + else { + opt = *p_opt; + } + + Rect src; + if (p_src == NULL) { + src = (Rect) {0, 0, texture->width, texture->height}; + } + else { + src = *p_src; + } + + Rect dest; + if (p_dest == NULL) { + dest = (Rect) {0, 0, video.windows[0].width, video.windows[0].height}; + } + else { + dest = *p_dest; + } + + GL(glBindTexture(GL_TEXTURE_2D, texture->name)); + + glBegin(GL_TRIANGLE_FAN); + + if (opt.doTint) { + glColor3ub(opt.tint.r, opt.tint.g, opt.tint.b); + } + else { + glColor3ub(255, 255, 255); + } + + glTexCoord2f( + ((float) src.x) / ((float) texture->actualWidth), + ((float) src.y) / ((float) texture->actualHeight) + ); + glVertex2i(dest.x, dest.y); + glTexCoord2f( + ((float) (src.x + src.w)) / ((float) texture->actualWidth), + ((float) src.y) / ((float) texture->actualHeight) + ); + glVertex2i(dest.x + dest.w, dest.y); + glTexCoord2f( + ((float) (src.x + src.w)) / ((float) texture->actualWidth), + ((float) (src.y + src.h)) / ((float) texture->actualHeight) + ); + glVertex2i(dest.x + dest.w, dest.y + dest.h); + glTexCoord2f( + ((float) src.x) / ((float) texture->actualWidth), + ((float) (src.y + src.h)) / ((float) texture->actualHeight) + ); + glVertex2i(dest.x, dest.y + dest.h); + GL(glEnd()); + + GL(glBindTexture(GL_TEXTURE_2D, 0)); +} + +void Backend_Begin(void) { + glViewport(0, 0, video.windows[0].aWidth, video.windows[0].aHeight); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); + GL(glMatrixMode(GL_MODELVIEW)); + GL(glLoadIdentity()); + GL(glMatrixMode(GL_PROJECTION)); + GL(glLoadIdentity()); + + float floatW = (float) video.windows[0].width; + float floatH = (float) video.windows[0].height; + + GL(glOrtho(0.0, floatW, floatH, 0.0, -1.0, 1.0)); +} + +void Backend_Clear(uint8_t r, uint8_t g, uint8_t b) { + glClearColor(((float) r) / 256.0, ((float) g) / 256.0, ((float) b) / 256.0, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void Backend_SetViewport(int x, int y, int w, int h) { + state.viewport.rect = (Rect) {x, y, w, h}; + GL(glScissor(x, video.windows[0].height - 1 - (y + h), w, h)); +} + +void Backend_EnableViewport(bool enable) { + state.viewport.enabled = enable; + + if (enable) { + GL(glEnable(GL_SCISSOR_TEST)); + } + else { + GL(glDisable(GL_SCISSOR_TEST)); + } +} + +void Backend_RenderRect(Rect rect, Colour colour) { + GL(glDisable(GL_TEXTURE_2D)); + glBegin(GL_TRIANGLE_FAN); + + if (state.alpha) { + glColor4ub(colour.r, colour.g, colour.b, colour.a); + } + else { + glColor3ub(colour.r, colour.g, colour.b); + } + + glVertex2i(rect.x, rect.y); + glVertex2i(rect.x + rect.w, rect.y); + glVertex2i(rect.x + rect.w, rect.y + rect.h); + glVertex2i(rect.x, rect.y + rect.h); + GL(glEnd()); + GL(glEnable(GL_TEXTURE_2D)); +} + +void Backend_RenderLine(Vec2 a, Vec2 b, Colour colour) { + GL(glDisable(GL_TEXTURE_2D)); + glBegin(GL_LINES); + glColor3ub(colour.r, colour.g, colour.b); + glVertex2i(a.x, a.y); + glVertex2i(b.x, b.y); + GL(glEnd()); + GL(glEnable(GL_TEXTURE_2D)); +} + +void Backend_EnableAlpha(bool enable) { + state.alpha = enable; + + if (enable) { + GL(glEnable(GL_BLEND)); + } + else { + GL(glDisable(GL_BLEND)); + } +} + +void Backend_FinishRender(void) { + GL(glFinish()); + SDL_GL_SwapWindow(video.windows[0].window); +} + +BackendViewport Backend_SaveViewport(void) { + return state.viewport; +} + +void Backend_RestoreViewport(BackendViewport viewport) { + state.viewport = viewport; + + Backend_SetViewport( + state.viewport.rect.x, state.viewport.rect.y, + state.viewport.rect.w, state.viewport.rect.h + ); + Backend_EnableViewport(state.viewport.enabled); +} + +#endif diff --git a/source/backends/glLegacy.h b/source/backends/glLegacy.h new file mode 100644 index 0000000000000000000000000000000000000000..0e5275fc0b61e9cb26cf8c92ed3e17c4edeec0c8 --- /dev/null +++ b/source/backends/glLegacy.h @@ -0,0 +1,34 @@ +#ifndef YT_BACKENDS_GL_LEGACY_H +#define YT_BACKENDS_GL_LEGACY_H + +#include "../common.h" + +#define GL_GLEXT_PROTOTYPES +#ifdef YT_BACKEND_GL_11 + #ifdef PLATFORM_OSX + #include <OpenGL/gl.h> + #include <OpenGL/glext.h> + #else + #include <GL/gl.h> + #include <GL/glext.h> + #endif +#endif + +#ifndef USE_KHR_DEBUG + #if !defined(NDEBUG) && defined(GL_KHR_debug) && GL_KHR_debug + #define USE_KHR_DEBUG 1 + #else + #define USE_KHR_DEBUG 0 + #endif +#endif + +typedef struct { + bool used; + GLuint name; + int width; + int height; + int actualWidth; + int actualHeight; +} Texture; + +#endif diff --git a/source/backends/stub.c b/source/backends/stub.c new file mode 100644 index 0000000000000000000000000000000000000000..cef9bcf20d6656bc713220bb20785b174ff581dd --- /dev/null +++ b/source/backends/stub.c @@ -0,0 +1,143 @@ +#include "../backend.h" + +#ifdef YT_BACKEND_STUB +#include "stub.h" + +void Backend_Init(bool beforeWindow) { + (void) beforeWindow; +} + +void Backend_Free(void) { + +} + +void Backend_SetTarget(Window* window) { + (void) window; +} + +Texture* Backend_LoadTexture(uint8_t* data, int width, int height, int ch) { + (void) data; + (void) width; + (void) height; + (void) ch; + return NULL; +} + +void Backend_FreeTexture(Texture* texture) { + (void) texture; +} + +Vec2 Backend_GetTextureSize(Texture* texture) { + (void) texture; + return (Vec2) {0, 0}; +} + +void Backend_RenderScene(void) { + +} + +void Backend_OnMapFree(void) { + +} + +void Backend_OnWindowResize(void) { + +} + +void Backend_RenderModel(Model* model, ModelRenderOpt* opt) { + (void) model; + (void) opt; +} + +void Backend_UseHoldModel(const char* path, ModelRenderOpt opt) { + (void) path; + (void) opt; +} + +void Backend_DrawTexture( + Texture* texture, TextureRenderOpt* p_opt, Rect* p_src, Rect* p_dest +) { + (void) texture; + + TextureRenderOpt opt; + if (p_opt == NULL) { + opt.doTint = false; + } + else { + opt = *p_opt; + } + + Rect src; + if (p_src == NULL) { + src = (Rect) {0, 0, texture->width, texture->height}; + } + else { + src = *p_src; + } + + Rect dest; + if (p_dest == NULL) { + dest = (Rect) {0, 0, video.width, video.height}; + } + else { + dest = *p_dest; + } +} + +void Backend_Begin(void) { + +} + +void Backend_Begin2D(bool originTop) { + (void) originTop; +} + +void Backend_Clear(uint8_t r, uint8_t g, uint8_t b) { + (void) r; + (void) g; + (void) b; +} + +void Backend_SetViewport(int x, int y, int w, int h) { + (void) x; + (void) y; + (void) w; + (void) h; +} + +void Backend_EnableViewport(bool enable) { + (void) enable; +} + +void Backend_RenderRect(Rect rect, Colour colour) { + (void) rect; + (void) colour; +} + +void Backend_RenderLine(Vec2 a, Vec2 b, Colour colour) { + (void) a; + (void) b; + (void) colour; +} + +void Backend_EnableAlpha(bool enable) { + (void) enable; +} + +void Backend_InitSkybox(void) { + +} + +void Backend_FinishRender(void) { + +} + +BackendViewport Backend_SaveViewport(void) { + return (BackendViewport) {}; +} + +void Backend_RestoreViewport(BackendViewport viewport) { + (void) viewport; +} + +#endif diff --git a/source/backends/stub.h b/source/backends/stub.h new file mode 100644 index 0000000000000000000000000000000000000000..77d457fc539a168878dc66b12a87fa2dce5163da --- /dev/null +++ b/source/backends/stub.h @@ -0,0 +1,8 @@ +#ifndef YT_BACKEND_STUB_H +#define YT_BACKEND_STUB_H + +typedef struct { + int empty; +} Texture; + +#endif diff --git a/source/common.h b/source/common.h new file mode 100644 index 0000000000000000000000000000000000000000..b417ea4ed6619f40724cfd5257a9858f536f5024 --- /dev/null +++ b/source/common.h @@ -0,0 +1,23 @@ +#ifndef YT_COMMON_H +#define YT_COMMON_H + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> +#include <math.h> + +#define PI 3.14159265359 + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + #define PLATFORM_WINDOWS +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(__linux__) + #define PLATFORM_LINUX +#elif defined(__unix__) + #define PLATFORM_UNIX +#endif + +#endif diff --git a/source/event/sdl2.c b/source/event/sdl2.c new file mode 100644 index 0000000000000000000000000000000000000000..83bb3197bef7035b87faeb128fd943670a43b67a --- /dev/null +++ b/source/event/sdl2.c @@ -0,0 +1,109 @@ +#include "../event.h" +#include "../video.h" +#include "../input/sdl.h" + +#ifdef YT_EVENT_SDL2 + +void Event_PrepareExternal(void) { + +} + +static int GetWindow(uint32_t id) { + for (int i = 0; i < WIN_NUM; ++ i) { + if (SDL_GetWindowID(video.windows[i].window) == id) { + return i; + } + } + + return -1; +} + +bool Event_PollExternal(Event* event) { + SDL_Event e; + + while (SDL_PollEvent(&e)) { + switch (e.type) { + case SDL_MOUSEMOTION: { + event->mouseMove = (Event_MouseMove) { + .type = EVENT_MOUSE_MOVE, + .window = GetWindow(e.motion.windowID), + .x = e.motion.x, + .y = e.motion.y, + .xRel = e.motion.xrel, + .yRel = e.motion.yrel + }; + break; + } + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: { + uint8_t button; + + switch (e.button.button) { + case SDL_BUTTON_LEFT: button = 0; break; + case SDL_BUTTON_MIDDLE: button = 1; break; + case SDL_BUTTON_RIGHT: button = 2; break; + default: button = 0; + } + + event->mouseButton = (Event_MouseButton) { + .type = e.type == SDL_MOUSEBUTTONDOWN? + EVENT_MOUSE_BUTTON_DOWN : EVENT_MOUSE_BUTTON_UP, + .window = GetWindow(e.button.windowID), + .button = button, + .x = e.button.x, + .y = e.button.y + }; + break; + } + case SDL_KEYUP: + case SDL_KEYDOWN: { + event->key = (Event_Key) { + .type = e.type == SDL_KEYDOWN? + EVENT_KEY_DOWN : EVENT_KEY_UP, + .key = Input_SDLScancodeToKey(e.key.keysym.scancode) + }; + break; + } + case SDL_QUIT: { + event->type = EVENT_QUIT; + break; + } + case SDL_WINDOWEVENT: { + if (e.window.event == SDL_WINDOWEVENT_RESIZED) { + event->windowResize = (Event_WindowResize) { + .type = EVENT_WINDOW_RESIZE, + .window = GetWindow(e.button.windowID), + .width = e.window.data1, + .height = e.window.data2 + }; + return true; + } + + continue; + } + case SDL_TEXTINPUT: { + Event_TextInput textInput; + textInput.type = EVENT_TEXT_INPUT; + strcpy(textInput.input, e.text.text); + + event->textInput = textInput; + break; + } + default: continue; + } + + return true; + } + + return false; +} + +void Event_StartTextInput(void) { + SDL_StartTextInput(); +} + +void Event_StopTextInput(void) { + SDL_StopTextInput(); +} + +#endif diff --git a/source/event.c b/source/event.c new file mode 100644 index 0000000000000000000000000000000000000000..c6a3f127f6874c39494c31991943d5fc6372468a --- /dev/null +++ b/source/event.c @@ -0,0 +1,96 @@ +#include <string.h> +#include "mem.h" +#include "util.h" +#include "event.h" +#include "input/sdl.h" + +Event events[EVENTS_AMOUNT]; + +typedef struct { + Event_Type type; + Event_Handler func; +} Handler; + +static Handler* handlers = NULL; +static size_t handlerNum = 0; + +void Event_Init(void) { + for (size_t i = 0; i < EVENTS_AMOUNT; ++ i) { + events[i].type = EVENT_NONE; + } +} + +void Event_Free(void) { + if (handlers) { + free(handlers); + } +} + +static int FindFree(void) { + int i; + + for (i = 0; i < EVENTS_AMOUNT; ++ i) { + if (events[i].type == EVENT_NONE) return i; + } + + for (i = 0; i < EVENTS_AMOUNT; ++ i) { + printf("Event %d: %d\n", i, (int) events[i].type); + } + + Error("Event pool full"); + return -1; +} + +void Event_Add(Event e) { + events[FindFree()] = e; +} + +void Event_Update(void) { + Event e; + + Event_PrepareExternal(); + while (Event_PollExternal(&e)) { + events[FindFree()] = e; + + for (size_t i = 0; i < handlerNum; ++ i) { + if (handlers[i].type == e.type) { + handlers[i].func(&e); + } + } + } +} + +bool Event_Available(void) { + for (size_t i = 0; i < EVENTS_AMOUNT; ++ i) { + if (events[i].type != EVENT_NONE) { + return true; + } + } + + return false; +} + +bool Event_Poll(Event* e) { + if (!Event_Available()) { + Event_Update(); + } + + for (size_t i = 0; i < EVENTS_AMOUNT; ++ i) { + if (events[i].type != EVENT_NONE) { + *e = events[i]; + events[i].type = EVENT_NONE; + return true; + } + } + + return false; +} + +void Event_AddHandler(Event_Type type, Event_Handler func) { + Handler handler = (Handler) {type, func}; + + ++ handlerNum; + handlers = SafeRealloc(handlers, handlerNum * sizeof(Handler)); + + handlers[handlerNum - 1] = handler; +} diff --git a/source/event.h b/source/event.h new file mode 100644 index 0000000000000000000000000000000000000000..b32e58db315dca59e76f5fa6a3912f7808818483 --- /dev/null +++ b/source/event.h @@ -0,0 +1,81 @@ +#ifndef YT_EVENT_H +#define YT_EVENT_H + +#include "window.h" +#include "keyboard.h" + +// i don't use an enum because of compiler warnings about missing cases in switch +// statements for event types +#define EVENT_NONE 0 +#define EVENT_MOUSE_MOVE 1 +#define EVENT_MOUSE_BUTTON_DOWN 2 +#define EVENT_MOUSE_BUTTON_UP 3 +#define EVENT_KEY_DOWN 4 +#define EVENT_KEY_UP 5 +#define EVENT_QUIT 6 +#define EVENT_WINDOW_RESIZE 7 +#define EVENT_TEXT_INPUT 8 + +typedef uint8_t Event_Type; + +typedef struct { + Event_Type type; + int window; + int x; + int y; + int xRel; + int yRel; +} Event_MouseMove; + +typedef struct { + Event_Type type; + int window; + uint8_t button; + int x; + int y; +} Event_MouseButton; + +typedef struct { + Event_Type type; + Key key; +} Event_Key; + +typedef struct { + Event_Type type; + int window; + int width; + int height; +} Event_WindowResize; + +typedef struct { + Event_Type type; + char input[60]; +} Event_TextInput; + +typedef union { + Event_Type type; + Event_MouseMove mouseMove; + Event_MouseButton mouseButton; + Event_Key key; + Event_WindowResize windowResize; + Event_TextInput textInput; +} Event; + +#define EVENTS_AMOUNT 32 +extern Event events[EVENTS_AMOUNT]; + +typedef void (*Event_Handler)(Event* event); + +void Event_Init(void); +void Event_Free(void); +void Event_Add(Event e); +void Event_Update(void); +void Event_PrepareExternal(void); +bool Event_PollExternal(Event* event); // defined in source/event/*.c +bool Event_Available(void); +bool Event_Poll(Event* e); +void Event_StartTextInput(void); // defined in source/event/*.c +void Event_StopTextInput(void); // defined in source/event/*.c +void Event_AddHandler(Event_Type type, Event_Handler func); + +#endif diff --git a/source/input/sdl.c b/source/input/sdl.c new file mode 100644 index 0000000000000000000000000000000000000000..a0ae7c9b3362f938832a7558184f2dba8860e3b4 --- /dev/null +++ b/source/input/sdl.c @@ -0,0 +1,222 @@ +#ifdef YT_INPUT_SDL2 +#include <SDL2/SDL.h> + +#include "sdl.h" +#include "../input.h" +#include "../keyboard.h" + +Key Input_SDLScancodeToKey(SDL_Scancode key) { + switch (key) { + case SDL_SCANCODE_A: return KEY_A; + case SDL_SCANCODE_B: return KEY_B; + case SDL_SCANCODE_C: return KEY_C; + case SDL_SCANCODE_D: return KEY_D; + case SDL_SCANCODE_E: return KEY_E; + case SDL_SCANCODE_F: return KEY_F; + case SDL_SCANCODE_G: return KEY_G; + case SDL_SCANCODE_H: return KEY_H; + case SDL_SCANCODE_I: return KEY_I; + case SDL_SCANCODE_J: return KEY_J; + case SDL_SCANCODE_K: return KEY_K; + case SDL_SCANCODE_L: return KEY_L; + case SDL_SCANCODE_M: return KEY_M; + case SDL_SCANCODE_N: return KEY_N; + case SDL_SCANCODE_O: return KEY_O; + case SDL_SCANCODE_P: return KEY_P; + case SDL_SCANCODE_Q: return KEY_Q; + case SDL_SCANCODE_R: return KEY_R; + case SDL_SCANCODE_S: return KEY_S; + case SDL_SCANCODE_T: return KEY_T; + case SDL_SCANCODE_U: return KEY_U; + case SDL_SCANCODE_V: return KEY_V; + case SDL_SCANCODE_W: return KEY_W; + case SDL_SCANCODE_X: return KEY_X; + case SDL_SCANCODE_Y: return KEY_Y; + case SDL_SCANCODE_Z: return KEY_Z; + case SDL_SCANCODE_0: return KEY_0; + case SDL_SCANCODE_1: return KEY_1; + case SDL_SCANCODE_2: return KEY_2; + case SDL_SCANCODE_3: return KEY_3; + case SDL_SCANCODE_4: return KEY_4; + case SDL_SCANCODE_5: return KEY_5; + case SDL_SCANCODE_6: return KEY_6; + case SDL_SCANCODE_7: return KEY_7; + case SDL_SCANCODE_8: return KEY_8; + case SDL_SCANCODE_9: return KEY_9; + case SDL_SCANCODE_RETURN: return KEY_RETURN; + case SDL_SCANCODE_ESCAPE: return KEY_ESCAPE; + case SDL_SCANCODE_BACKSPACE: return KEY_BACKSPACE; + case SDL_SCANCODE_TAB: return KEY_TAB; + case SDL_SCANCODE_SPACE: return KEY_SPACE; + case SDL_SCANCODE_MINUS: return KEY_MINUS; + case SDL_SCANCODE_EQUALS: return KEY_EQUALS; + case SDL_SCANCODE_LEFTBRACKET: return KEY_LEFT_BRACKET; + case SDL_SCANCODE_RIGHTBRACKET: return KEY_RIGHT_BRACKET; + case SDL_SCANCODE_BACKSLASH: return KEY_BACKSLASH; + case SDL_SCANCODE_SEMICOLON: return KEY_SEMICOLON; + case SDL_SCANCODE_APOSTROPHE: return KEY_APOSTROPHE; + case SDL_SCANCODE_GRAVE: return KEY_GRAVE; + case SDL_SCANCODE_COMMA: return KEY_COMMA; + case SDL_SCANCODE_PERIOD: return KEY_PERIOD; + case SDL_SCANCODE_SLASH: return KEY_SLASH; + case SDL_SCANCODE_CAPSLOCK: return KEY_CAPS_LOCK; + case SDL_SCANCODE_F1: return KEY_F1; + case SDL_SCANCODE_F2: return KEY_F2; + case SDL_SCANCODE_F3: return KEY_F3; + case SDL_SCANCODE_F4: return KEY_F4; + case SDL_SCANCODE_F5: return KEY_F5; + case SDL_SCANCODE_F6: return KEY_F6; + case SDL_SCANCODE_F7: return KEY_F7; + case SDL_SCANCODE_F8: return KEY_F8; + case SDL_SCANCODE_F9: return KEY_F9; + case SDL_SCANCODE_F10: return KEY_F10; + case SDL_SCANCODE_F11: return KEY_F11; + case SDL_SCANCODE_F12: return KEY_F12; + case SDL_SCANCODE_PRINTSCREEN: return KEY_PRINT_SCREEN; + case SDL_SCANCODE_SCROLLLOCK: return KEY_SCROLL_LOCK; + case SDL_SCANCODE_PAUSE: return KEY_PAUSE; + case SDL_SCANCODE_INSERT: return KEY_INSERT; + case SDL_SCANCODE_HOME: return KEY_HOME; + case SDL_SCANCODE_PAGEUP: return KEY_PAGE_UP; + case SDL_SCANCODE_PAGEDOWN: return KEY_PAGE_DOWN; + case SDL_SCANCODE_DELETE: return KEY_DELETE; + case SDL_SCANCODE_END: return KEY_END; + case SDL_SCANCODE_RIGHT: return KEY_RIGHT; + case SDL_SCANCODE_LEFT: return KEY_LEFT; + case SDL_SCANCODE_DOWN: return KEY_DOWN; + case SDL_SCANCODE_UP: return KEY_UP; + case SDL_SCANCODE_NUMLOCKCLEAR: return KEY_NUM_LOCK; + case SDL_SCANCODE_KP_DIVIDE: return KEY_NP_DIVIDE; + case SDL_SCANCODE_KP_MULTIPLY: return KEY_NP_MULTIPLY; + case SDL_SCANCODE_KP_MINUS: return KEY_NP_MINUS; + case SDL_SCANCODE_KP_PLUS: return KEY_NP_PLUS; + case SDL_SCANCODE_KP_ENTER: return KEY_NP_ENTER; + case SDL_SCANCODE_KP_0: return KEY_NP_0; + case SDL_SCANCODE_KP_1: return KEY_NP_1; + case SDL_SCANCODE_KP_2: return KEY_NP_2; + case SDL_SCANCODE_KP_3: return KEY_NP_3; + case SDL_SCANCODE_KP_4: return KEY_NP_4; + case SDL_SCANCODE_KP_5: return KEY_NP_5; + case SDL_SCANCODE_KP_6: return KEY_NP_6; + case SDL_SCANCODE_KP_7: return KEY_NP_7; + case SDL_SCANCODE_KP_8: return KEY_NP_8; + case SDL_SCANCODE_KP_9: return KEY_NP_9; + case SDL_SCANCODE_KP_PERIOD: return KEY_NP_PERIOD; + default: return 0; + } +} + +SDL_Scancode Input_KeyToSDLScancode(Key key) { + switch (key) { + case KEY_A: return SDL_SCANCODE_A; + case KEY_B: return SDL_SCANCODE_B; + case KEY_C: return SDL_SCANCODE_C; + case KEY_D: return SDL_SCANCODE_D; + case KEY_E: return SDL_SCANCODE_E; + case KEY_F: return SDL_SCANCODE_F; + case KEY_G: return SDL_SCANCODE_G; + case KEY_H: return SDL_SCANCODE_H; + case KEY_I: return SDL_SCANCODE_I; + case KEY_J: return SDL_SCANCODE_J; + case KEY_K: return SDL_SCANCODE_K; + case KEY_L: return SDL_SCANCODE_L; + case KEY_M: return SDL_SCANCODE_M; + case KEY_N: return SDL_SCANCODE_N; + case KEY_O: return SDL_SCANCODE_O; + case KEY_P: return SDL_SCANCODE_P; + case KEY_Q: return SDL_SCANCODE_Q; + case KEY_R: return SDL_SCANCODE_R; + case KEY_S: return SDL_SCANCODE_S; + case KEY_T: return SDL_SCANCODE_T; + case KEY_U: return SDL_SCANCODE_U; + case KEY_V: return SDL_SCANCODE_V; + case KEY_W: return SDL_SCANCODE_W; + case KEY_X: return SDL_SCANCODE_X; + case KEY_Y: return SDL_SCANCODE_Y; + case KEY_Z: return SDL_SCANCODE_Z; + case KEY_0: return SDL_SCANCODE_0; + case KEY_1: return SDL_SCANCODE_1; + case KEY_2: return SDL_SCANCODE_2; + case KEY_3: return SDL_SCANCODE_3; + case KEY_4: return SDL_SCANCODE_4; + case KEY_5: return SDL_SCANCODE_5; + case KEY_6: return SDL_SCANCODE_6; + case KEY_7: return SDL_SCANCODE_7; + case KEY_8: return SDL_SCANCODE_8; + case KEY_9: return SDL_SCANCODE_9; + case KEY_RETURN: return SDL_SCANCODE_RETURN; + case KEY_ESCAPE: return SDL_SCANCODE_ESCAPE; + case KEY_BACKSPACE: return SDL_SCANCODE_BACKSPACE; + case KEY_TAB: return SDL_SCANCODE_TAB; + case KEY_SPACE: return SDL_SCANCODE_SPACE; + case KEY_MINUS: return SDL_SCANCODE_MINUS; + case KEY_EQUALS: return SDL_SCANCODE_EQUALS; + case KEY_LEFT_BRACKET: return SDL_SCANCODE_LEFTBRACKET; + case KEY_RIGHT_BRACKET: return SDL_SCANCODE_RIGHTBRACKET; + case KEY_BACKSLASH: return SDL_SCANCODE_BACKSLASH; + case KEY_SEMICOLON: return SDL_SCANCODE_SEMICOLON; + case KEY_APOSTROPHE: return SDL_SCANCODE_APOSTROPHE; + case KEY_GRAVE: return SDL_SCANCODE_GRAVE; + case KEY_COMMA: return SDL_SCANCODE_COMMA; + case KEY_PERIOD: return SDL_SCANCODE_PERIOD; + case KEY_SLASH: return SDL_SCANCODE_SLASH; + case KEY_CAPS_LOCK: return SDL_SCANCODE_CAPSLOCK; + case KEY_F1: return SDL_SCANCODE_F1; + case KEY_F2: return SDL_SCANCODE_F2; + case KEY_F3: return SDL_SCANCODE_F3; + case KEY_F4: return SDL_SCANCODE_F4; + case KEY_F5: return SDL_SCANCODE_F5; + case KEY_F6: return SDL_SCANCODE_F6; + case KEY_F7: return SDL_SCANCODE_F7; + case KEY_F8: return SDL_SCANCODE_F8; + case KEY_F9: return SDL_SCANCODE_F9; + case KEY_F10: return SDL_SCANCODE_F10; + case KEY_F11: return SDL_SCANCODE_F11; + case KEY_F12: return SDL_SCANCODE_F12; + case KEY_PRINT_SCREEN: return SDL_SCANCODE_PRINTSCREEN; + case KEY_SCROLL_LOCK: return SDL_SCANCODE_SCROLLLOCK; + case KEY_PAUSE: return SDL_SCANCODE_PAUSE; + case KEY_INSERT: return SDL_SCANCODE_INSERT; + case KEY_HOME: return SDL_SCANCODE_HOME; + case KEY_PAGE_UP: return SDL_SCANCODE_PAGEUP; + case KEY_PAGE_DOWN: return SDL_SCANCODE_PAGEDOWN; + case KEY_DELETE: return SDL_SCANCODE_DELETE; + case KEY_END: return SDL_SCANCODE_END; + case KEY_RIGHT: return SDL_SCANCODE_RIGHT; + case KEY_LEFT: return SDL_SCANCODE_LEFT; + case KEY_DOWN: return SDL_SCANCODE_DOWN; + case KEY_UP: return SDL_SCANCODE_UP; + case KEY_NUM_LOCK: return SDL_SCANCODE_NUMLOCKCLEAR; + case KEY_NP_DIVIDE: return SDL_SCANCODE_KP_DIVIDE; + case KEY_NP_MULTIPLY: return SDL_SCANCODE_KP_MULTIPLY; + case KEY_NP_MINUS: return SDL_SCANCODE_KP_MINUS; + case KEY_NP_PLUS: return SDL_SCANCODE_KP_PLUS; + case KEY_NP_ENTER: return SDL_SCANCODE_KP_ENTER; + case KEY_NP_0: return SDL_SCANCODE_KP_0; + case KEY_NP_1: return SDL_SCANCODE_KP_1; + case KEY_NP_2: return SDL_SCANCODE_KP_2; + case KEY_NP_3: return SDL_SCANCODE_KP_3; + case KEY_NP_4: return SDL_SCANCODE_KP_4; + case KEY_NP_5: return SDL_SCANCODE_KP_5; + case KEY_NP_6: return SDL_SCANCODE_KP_6; + case KEY_NP_7: return SDL_SCANCODE_KP_7; + case KEY_NP_8: return SDL_SCANCODE_KP_8; + case KEY_NP_9: return SDL_SCANCODE_KP_9; + case KEY_NP_PERIOD: return SDL_SCANCODE_KP_PERIOD; + default: return 0; + } +} + +bool Input_KeyPressed(Key key) { + const uint8_t* keyState = SDL_GetKeyboardState(NULL); + + return keyState[Input_KeyToSDLScancode(key)]? true : false; +} + +bool Input_MouseButtonPressed(uint8_t button) { + int x, y; + + return (SDL_GetMouseState(&x, &y) & SDL_BUTTON(button))? true : false; +} + +#endif diff --git a/source/input/sdl.h b/source/input/sdl.h new file mode 100644 index 0000000000000000000000000000000000000000..2697a4b616517636405e11c6d54a781c860c6c48 --- /dev/null +++ b/source/input/sdl.h @@ -0,0 +1,9 @@ +#if !defined(YT_INPUT_SDL_H) && defined(YT_INPUT_SDL2) + +#include <SDL2/SDL.h> +#include "../keyboard.h" + +Key Input_SDLScancodeToKey(SDL_Scancode key); +SDL_Scancode Input_KeyToSDLScancode(Key key); + +#endif diff --git a/source/input.c b/source/input.c new file mode 100644 index 0000000000000000000000000000000000000000..6e4a070987ff8c3063e541f36e15750596eaceaf --- /dev/null +++ b/source/input.c @@ -0,0 +1,127 @@ +#include <stdio.h> +#include <string.h> +#include "mem.h" +#include "input.h" + +Input_Manager input = { + .binds = NULL, + .bindAmount = 0, + + .mouseBtn = {false, false, false}, + .mousePos = {0, 0} +}; + +void Input_Free(void) { + if (input.binds) { + free(input.binds); + input.binds = NULL; + } + + input.bindAmount = 0; +} + +void Input_PrintBind(char* dest, size_t size, Input_BindID bindID) { + Input_Bind* bind = &input.binds[bindID]; + + switch (bind->type) { + case YT_INPUT_BIND_KEY: { + snprintf(dest, size, "key %s", Key_ToString(bind->key.key)); + break; + } + case YT_INPUT_BIND_MOUSE_BUTTON: { + snprintf(dest, size, "button %d", bind->mouseButton.button); + break; + } + default: { + snprintf(dest, size, "???"); + break; + } + } +} + +bool Input_MatchBind(Input_BindID bindID, Event* e) { + if (bindID > input.bindAmount) return false; + + Input_Bind* bind = &input.binds[bindID]; + + switch (bind->type) { + case YT_INPUT_BIND_KEY: { + if ((e->type != EVENT_KEY_DOWN) && (e->type != EVENT_KEY_UP)) { + return false; + } + + return bind->key.key == e->key.key; // TODO: modifiers + } + case YT_INPUT_BIND_MOUSE_BUTTON: { + if ( + (e->type != EVENT_MOUSE_BUTTON_DOWN) && + (e->type != EVENT_MOUSE_BUTTON_UP) + ) { + return false; + } + + return bind->mouseButton.button == e->mouseButton.button; + } + } + + return false; +} + +bool Input_BindPressed(Input_BindID bindID) { + if (bindID > input.bindAmount) return false; + + Input_Bind* bind = &input.binds[bindID]; + + switch (bind->type) { + case YT_INPUT_BIND_KEY: { + return Input_KeyPressed(bind->key.key); // TODO: modifiers + } + case YT_INPUT_BIND_MOUSE_BUTTON: { + return Input_MouseButtonPressed(bind->mouseButton.button); + } + } + + return false; +} + +Input_BindID Input_AddBind(Input_Bind bind) { + input.binds = SafeRealloc(input.binds, (input.bindAmount + 1) * sizeof(Input_Bind)); + ++ input.bindAmount; + + input.binds[input.bindAmount - 1] = bind; + return input.bindAmount - 1; +} + +Input_BindID Input_AddKeyBind(Key mod[3], Key key) { + Input_Bind bind; + bind.type = YT_INPUT_BIND_KEY; + bind.key.key = key; + memcpy(bind.key.mod, mod, sizeof(bind.key.mod)); + + return Input_AddBind(bind); +} + +Input_BindID Input_AddMouseButtonBind(uint8_t button) { + Input_Bind bind; + bind.type = YT_INPUT_BIND_MOUSE_BUTTON; + bind.mouseButton.button = button; + + return Input_AddBind(bind); +} + +void Input_HandleEvent(Event* e) { + switch (e->type) { + case EVENT_MOUSE_MOVE: { + input.mousePos = (Vec2) {e->mouseMove.x, e->mouseMove.y}; + break; + } + case EVENT_MOUSE_BUTTON_DOWN: { + input.mouseBtn[e->mouseButton.button] = true; + break; + } + case EVENT_MOUSE_BUTTON_UP: { + input.mouseBtn[e->mouseButton.button] = false; + break; + } + } +} diff --git a/source/input.h b/source/input.h new file mode 100644 index 0000000000000000000000000000000000000000..073849888e907cb9dde41be4ad318a203a17f787 --- /dev/null +++ b/source/input.h @@ -0,0 +1,65 @@ +#ifndef YT_INPUT_H +#define YT_INPUT_H + +#include "event.h" +#include "types.h" +#include "keyboard.h" + +// mouse stuff +#define YT_MOUSE_BUTTON_1 0 +#define YT_MOUSE_BUTTON_2 1 +#define YT_MOUSE_BUTTON_3 2 + +typedef enum { + YT_INPUT_BIND_KEY = 0, + YT_INPUT_BIND_MOUSE_BUTTON +} Input_BindType; + +typedef struct { + Input_BindType type; + + Key key; + Key mod[3]; +} Input_KeyBind; + +typedef struct { + Input_BindType type; + uint8_t button; +} Input_MouseButtonBind; + +typedef union { + Input_BindType type; + Input_KeyBind key; + Input_MouseButtonBind mouseButton; +} Input_Bind; + +typedef struct { + Input_Bind* binds; + size_t bindAmount; + + bool mouseBtn[3]; + Vec2 mousePos; +} Input_Manager; + +typedef size_t Input_BindID; + +#define INPUT_BIND_NONE ((size_t) -1) + +extern Input_Manager input; + +// non-portable +bool Input_KeyPressed(Key key); +bool Input_MouseButtonPressed(uint8_t button); + +// portable +// NOTE: NOT SAFE +void Input_Free(void); +void Input_PrintBind(char* dest, size_t size, Input_BindID bindID); +bool Input_MatchBind(Input_BindID bindID, Event* e); +bool Input_BindPressed(Input_BindID bindID); +Input_BindID Input_AddBind(Input_Bind bind); +Input_BindID Input_AddKeyBind(Key mod[3], Key key); +Input_BindID Input_AddMouseButtonBind(uint8_t button); +void Input_HandleEvent(Event* e); + +#endif diff --git a/source/keyboard.c b/source/keyboard.c new file mode 100644 index 0000000000000000000000000000000000000000..e64afd863e5cda1be66f554700b3f05b0d53327f --- /dev/null +++ b/source/keyboard.c @@ -0,0 +1,118 @@ +#include <string.h> +#include "keyboard.h" + +// TODO: this thing has missing values +static const char* keyTable[] = { + /* 0 */ /* KEY_NONE */ "None", + /* 1 */ /* KEY_A */ "A", + /* 2 */ /* KEY_B */ "B", + /* 3 */ /* KEY_C */ "C", + /* 4 */ /* KEY_D */ "D", + /* 5 */ /* KEY_E */ "E", + /* 6 */ /* KEY_F */ "F", + /* 7 */ /* KEY_G */ "G", + /* 8 */ /* KEY_H */ "H", + /* 9 */ /* KEY_I */ "I", + /* 10 */ /* KEY_J */ "J", + /* 11 */ /* KEY_K */ "K", + /* 12 */ /* KEY_L */ "L", + /* 13 */ /* KEY_M */ "M", + /* 14 */ /* KEY_N */ "N", + /* 15 */ /* KEY_O */ "O", + /* 16 */ /* KEY_P */ "P", + /* 17 */ /* KEY_Q */ "Q", + /* 18 */ /* KEY_R */ "R", + /* 19 */ /* KEY_S */ "S", + /* 20 */ /* KEY_T */ "T", + /* 21 */ /* KEY_U */ "U", + /* 22 */ /* KEY_V */ "V", + /* 23 */ /* KEY_W */ "W", + /* 24 */ /* KEY_X */ "X", + /* 25 */ /* KEY_Y */ "Y", + /* 26 */ /* KEY_Z */ "Z", + /* 27 */ /* KEY_0 */ "0", + /* 28 */ /* KEY_1 */ "1", + /* 29 */ /* KEY_2 */ "2", + /* 30 */ /* KEY_3 */ "3", + /* 31 */ /* KEY_4 */ "4", + /* 32 */ /* KEY_5 */ "5", + /* 33 */ /* KEY_6 */ "6", + /* 34 */ /* KEY_7 */ "7", + /* 35 */ /* KEY_8 */ "8", + /* 36 */ /* KEY_9 */ "9", + /* 37 */ /* KEY_RETURN */ "Return", + /* 38 */ /* KEY_ESCAPE */ "Escape", + /* 39 */ /* KEY_BACKSPACE */ "Backspace", + /* 40 */ /* KEY_TAB */ "Tab", + /* 41 */ /* KEY_SPACE */ "Space", + /* 42 */ /* KEY_MINUS */ "Minus", + /* 43 */ /* KEY_EQUALS */ "Equals", + /* 44 */ /* KEY_LEFT_BRACKET */ "Left bracket", + /* 45 */ /* KEY_RIGHT_BRACKET */ "Right bracket", + /* 46 */ /* KEY_BACKSLASH */ "Backslash", + /* 47 */ /* KEY_SEMICOLON */ "Semicolon", + /* 48 */ /* KEY_APOSTROPHE */ "Apostrophe", + /* 49 */ /* KEY_GRAVE */ "Grave", + /* 50 */ /* KEY_COMMA */ "Comma", + /* 51 */ /* KEY_PERIOD */ "Period", + /* 52 */ /* KEY_SLASH */ "Slash", + /* 53 */ /* KEY_CAPS_LOCK */ "Caps lock", + /* 54 */ /* KEY_F1 */ "F1", + /* 55 */ /* KEY_F2 */ "F2", + /* 56 */ /* KEY_F3 */ "F3", + /* 57 */ /* KEY_F4 */ "F4", + /* 58 */ /* KEY_F5 */ "F5", + /* 59 */ /* KEY_F6 */ "F6", + /* 60 */ /* KEY_F7 */ "F7", + /* 61 */ /* KEY_F8 */ "F8", + /* 62 */ /* KEY_F9 */ "F9", + /* 63 */ /* KEY_F10 */ "F10", + /* 64 */ /* KEY_F11 */ "F11", + /* 65 */ /* KEY_F12 */ "F12", + /* 66 */ /* KEY_PRINT_SCREEN */ "Print screen", + /* 67 */ /* KEY_SCROLL_LOCK */ "Scroll lock", + /* 68 */ /* KEY_PAUSE */ "Pause", + /* 69 */ /* KEY_INSERT */ "Insert", + /* 70 */ /* KEY_HOME */ "Home", + /* 71 */ /* KEY_PAGE_UP */ "Page up", + /* 72 */ /* KEY_PAGE_DOWN */ "Page down", + /* 73 */ /* KEY_DELETE */ "Delete", + /* 74 */ /* KEY_END */ "End", + /* 75 */ /* KEY_RIGHT */ "Right arrow", + /* 76 */ /* KEY_LEFT */ "Left arrow", + /* 77 */ /* KEY_DOWN */ "Down arrow", + /* 78 */ /* KEY_UP */ "Up arrow", + /* 79 */ /* KEY_NUM_LOCK */ "Num lock", + /* 80 */ /* KEY_NP_DIVIDE */ "Numpad divide", + /* 81 */ /* KEY_NP_MULTIPLY */ "Numpad multiply", + /* 82 */ /* KEY_NP_MINUS */ "Numpad minus", + /* 83 */ /* KEY_NP_PLUS */ "Numpad plus", + /* 84 */ /* KEY_NP_ENTER */ "Numpad enter", + /* 85 */ /* KEY_NP_0 */ "Numpad 0", + /* 86 */ /* KEY_NP_1 */ "Numpad 1", + /* 87 */ /* KEY_NP_2 */ "Numpad 2", + /* 88 */ /* KEY_NP_3 */ "Numpad 3", + /* 89 */ /* KEY_NP_4 */ "Numpad 4", + /* 90 */ /* KEY_NP_5 */ "Numpad 5", + /* 91 */ /* KEY_NP_6 */ "Numpad 6", + /* 92 */ /* KEY_NP_7 */ "Numpad 7", + /* 93 */ /* KEY_NP_8 */ "Numpad 8", + /* 94 */ /* KEY_NP_9 */ "Numpad 9", + /* 95 */ /* KEY_NP_PERIOD */ "Numpad period" +}; + +const char* Key_ToString(Key key) { + if ((key < 0) || (key >= KEY_AMOUNT)) { + return "???"; + } + + return keyTable[key]; +} + +Key Key_FromString(const char* str) { + for (size_t i = 0; i < KEY_AMOUNT; ++ i) { + if (strcmp(keyTable[i], str) == 0) return (Key) i; + } + + return 0; +} diff --git a/source/keyboard.h b/source/keyboard.h new file mode 100644 index 0000000000000000000000000000000000000000..68af91092313b988efa73a849bd574b144b976c1 --- /dev/null +++ b/source/keyboard.h @@ -0,0 +1,118 @@ +#ifndef YT_KEYBOARD_H +#define YT_KEYBOARD_H + +#include "common.h" + +typedef enum { + KEY_NONE = 0, + + KEY_A = 1, + KEY_B = 2, + KEY_C = 3, + KEY_D = 4, + KEY_E = 5, + KEY_F = 6, + KEY_G = 7, + KEY_H = 8, + KEY_I = 9, + KEY_J = 10, + KEY_K = 11, + KEY_L = 12, + KEY_M = 13, + KEY_N = 14, + KEY_O = 15, + KEY_P = 16, + KEY_Q = 17, + KEY_R = 18, + KEY_S = 19, + KEY_T = 20, + KEY_U = 21, + KEY_V = 22, + KEY_W = 23, + KEY_X = 24, + KEY_Y = 25, + KEY_Z = 26, + + KEY_0 = 27, + KEY_1 = 28, + KEY_2 = 29, + KEY_3 = 30, + KEY_4 = 31, + KEY_5 = 32, + KEY_6 = 33, + KEY_7 = 34, + KEY_8 = 35, + KEY_9 = 36, + + KEY_RETURN = 37, + KEY_ESCAPE = 38, + KEY_BACKSPACE = 39, + KEY_TAB = 40, + KEY_SPACE = 41, + KEY_MINUS = 42, + KEY_EQUALS = 43, + KEY_LEFT_BRACKET = 44, + KEY_RIGHT_BRACKET = 45, + KEY_BACKSLASH = 46, + KEY_SEMICOLON = 47, + KEY_APOSTROPHE = 48, + KEY_GRAVE = 49, + KEY_COMMA = 50, + KEY_PERIOD = 51, + KEY_SLASH = 52, + KEY_CAPS_LOCK = 53, + + KEY_F1 = 54, + KEY_F2 = 55, + KEY_F3 = 56, + KEY_F4 = 57, + KEY_F5 = 58, + KEY_F6 = 59, + KEY_F7 = 60, + KEY_F8 = 61, + KEY_F9 = 62, + KEY_F10 = 63, + KEY_F11 = 64, + KEY_F12 = 65, + + KEY_PRINT_SCREEN = 66, + KEY_SCROLL_LOCK = 67, + KEY_PAUSE = 68, + KEY_INSERT = 69, + + KEY_HOME = 70, + KEY_PAGE_UP = 71, + KEY_PAGE_DOWN = 72, + KEY_DELETE = 73, + KEY_END = 74, + KEY_RIGHT = 75, + KEY_LEFT = 76, + KEY_DOWN = 77, + KEY_UP = 78, + + KEY_NUM_LOCK = 79, + + KEY_NP_DIVIDE = 80, + KEY_NP_MULTIPLY = 81, + KEY_NP_MINUS = 82, + KEY_NP_PLUS = 83, + KEY_NP_ENTER = 84, + KEY_NP_0 = 85, + KEY_NP_1 = 86, + KEY_NP_2 = 87, + KEY_NP_3 = 88, + KEY_NP_4 = 89, + KEY_NP_5 = 90, + KEY_NP_6 = 91, + KEY_NP_7 = 92, + KEY_NP_8 = 93, + KEY_NP_9 = 94, + KEY_NP_PERIOD = 95, + + KEY_AMOUNT +} Key; + +const char* Key_ToString(Key key); +Key Key_FromString(const char* str); + +#endif diff --git a/source/log.c b/source/log.c new file mode 100644 index 0000000000000000000000000000000000000000..1370d53de631952710cbdb1081e8c56a9bac5d87 --- /dev/null +++ b/source/log.c @@ -0,0 +1,121 @@ +#include <time.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include "log.h" +#include "mem.h" +#include "window.h" + +static FILE* logFile = NULL; + +void Log_Init(void) { +// logFile = fopen(AE_LOCATION "log.txt", "w"); +// +// if (!logFile) { +// Warn("Cannot open log file: " AE_LOCATION "log.txt"); +// } +} + +void Log_End(void) { + // if (!logFile) { + // fflush(logFile); + // fclose(logFile); + // } +} + +// most of this is taken from vsprintf(3) +#define FMT(RET, FORMAT) do { \ + int n = 0; \ + size_t size = 0; \ + va_list ap; \ +\ + va_start(ap, FORMAT); \ + n = vsnprintf(RET, size, FORMAT, ap); \ + va_end(ap); \ +\ + assert(n >= 0); \ +\ + size = n + 1; \ + RET = (char*) SafeMalloc(size); \ + if (RET == NULL) { \ + return; \ + } \ +\ + va_start(ap, FORMAT); \ + n = vsnprintf(RET, size, FORMAT, ap); \ + va_end(ap); \ +\ + assert(n >= 0); \ +} while (0); + +void Log(const char* format, ...) { + char* ret = NULL; + + FMT(ret, format); + + time_t rawTime; + struct tm* tm; + + time(&rawTime); + tm = localtime(&rawTime); + + printf("[%.2d:%.2d:%.2d] %s\n", tm->tm_hour, tm->tm_min, tm->tm_sec, ret); + if (logFile) { + fprintf( + logFile, "[%.2d:%.2d:%.2d] %s\n", tm->tm_hour, tm->tm_min, tm->tm_sec, + ret + ); + fflush(logFile); + } + + free(ret); +} + +void Error(const char* format, ...) { + char* ret = NULL; + + FMT(ret, format); + + time_t rawTime; + struct tm* tm; + + time(&rawTime); + tm = localtime(&rawTime); + + printf("[%.2d:%.2d:%.2d] %s\n", tm->tm_hour, tm->tm_min, tm->tm_sec, ret); + if (logFile) { + fprintf( + logFile, "[%.2d:%.2d:%.2d] %s\n", tm->tm_hour, tm->tm_min, tm->tm_sec, + ret + ); + fflush(logFile); + } + + Window_MessageBox(WINDOW_MSG_ERROR, "Fatal error", ret); + free(ret); + exit(1); +} + +void Warn(const char* format, ...) { + char* ret = NULL; + + FMT(ret, format); + + time_t rawTime; + struct tm* tm; + + time(&rawTime); + tm = localtime(&rawTime); + + printf("[%.2d:%.2d:%.2d] %s\n", tm->tm_hour, tm->tm_min, tm->tm_sec, ret); + if (logFile) { + fprintf( + logFile, "[%.2d:%.2d:%.2d] %s\n", tm->tm_hour, tm->tm_min, tm->tm_sec, + ret + ); + fflush(logFile); + } + + Window_MessageBox(WINDOW_MSG_WARNING, "Warning", ret); + free(ret); +} diff --git a/source/log.h b/source/log.h new file mode 100644 index 0000000000000000000000000000000000000000..61d7a2c15d8e3524b6a9edfe980798324053b6c2 --- /dev/null +++ b/source/log.h @@ -0,0 +1,10 @@ +#ifndef YT_LOG_H +#define YT_LOG_H + +void Log_Init(void); +void Log_End(void); +void Log(const char* format, ...); +void Error(const char* format, ...); +void Warn(const char* format, ...); + +#endif diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000000000000000000000000000000000000..061ed7eefd8af6f58434de4e2c9fec463f6bf076 --- /dev/null +++ b/source/main.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} diff --git a/source/mem.c b/source/mem.c new file mode 100644 index 0000000000000000000000000000000000000000..bcef6460e47b9108ddcc2f316a9836fd77855b03 --- /dev/null +++ b/source/mem.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include "mem.h" +#include "common.h" + +void* SafeMalloc(size_t size) { + if (size == 0) return NULL; + + void* ret = malloc(size); + + if (ret == NULL) { + fprintf(stderr, "Malloc returned NULL\n"); + exit(1); + } + + return ret; +} + +void* SafeRealloc(void* ptr, size_t size) { + void* ret = realloc(ptr, size); + + if ((ret == NULL) && (size > 0)) { + fprintf(stderr, "Malloc returned NULL\n"); + exit(1); + } + + return ret; +} diff --git a/source/mem.h b/source/mem.h new file mode 100644 index 0000000000000000000000000000000000000000..893c10a9495d957ef276fd4cd03e691afbc2b6c5 --- /dev/null +++ b/source/mem.h @@ -0,0 +1,11 @@ +#ifndef YT_MEM_H +#define YT_MEM_H + +#include "common.h" + +#define NEW(T) (T*) SafeMalloc(sizeof(T)) + +void* SafeMalloc(size_t size); +void* SafeRealloc(void* ptr, size_t size); + +#endif diff --git a/source/platform/unix.c b/source/platform/unix.c new file mode 100644 index 0000000000000000000000000000000000000000..0aebddacddcfbf87717fc98f0f4b2f6036917ebe --- /dev/null +++ b/source/platform/unix.c @@ -0,0 +1,20 @@ +#include "../platform.h" + +#if defined(PLATFORM_UNIX) || defined(PLATFORM_LINUX) +#include <time.h> + +void Platform_Init(void) { + +} + +void Platform_Quit(void) { + +} + +uint64_t Platform_GetTime(void) { + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + return time.tv_sec * 1000000 + time.tv_nsec / 1000; +} + +#endif diff --git a/source/platform/windows.c b/source/platform/windows.c new file mode 100644 index 0000000000000000000000000000000000000000..7ffdf922ebe4bba533e7ed668984c87316a48a12 --- /dev/null +++ b/source/platform/windows.c @@ -0,0 +1,46 @@ +#include "../platform.h" + +#ifdef PLATFORM_WINDOWS +#include <windows.h> + +static UINT timerRes = 1; +static LARGE_INTEGER perfFreq; + +void Platform_Init(void) { + // increase the timer res to the max to make timing stuff and waiting + // more accurate + TIMECAPS tc; + + if (timeGetDevCaps(&tc, sizeof(tc)) != TIMERR_NOERROR) { + if (timerRes < tc.wPeriodMin) { + timerRes = tc.wPeriodMin; + } + else if (timerRes > tc.wPeriodMax) { + timerRes = tc.wPeriodMax; + } + } + timeBeginPeriod(timerRes); + + LARGE_INTEGER perfFreq; + uint64_t perfMul = 1000000; + QueryPerformanceFrequency(&perfFreq); + while (!(perfFreq.QuadPart % 10) && !(perfMul % 10)) { + perfFreq.QuadPart /= 10; + perfMul /= 10; + } + + // put the timer res back to the original value before exiting + timeEndPeriod(timerRes); +} + +void Platform_Quit(void) { + timeEndPeriod(timerRes); +} + +uint64_t Platform_GetTime(void) { + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + return time.QuadPart * perfctmul / perfFreq.QuadPart; +} + +#endif diff --git a/source/platform.h b/source/platform.h new file mode 100644 index 0000000000000000000000000000000000000000..27962be4902bfb840b45aa65c2675e3891344f1a --- /dev/null +++ b/source/platform.h @@ -0,0 +1,10 @@ +#ifndef YT_PLATFORM_H +#define YT_PLATFORM_H + +#include "common.h" + +void Platform_Init(void); +void Platform_Quit(void); +uint64_t Platform_GetTime(void); + +#endif diff --git a/source/stb.c b/source/stb.c new file mode 100644 index 0000000000000000000000000000000000000000..3ccc10ee72e8f8072442ee5d160c1ba1f9686f3b --- /dev/null +++ b/source/stb.c @@ -0,0 +1,7 @@ +#define STB_IMAGE_IMPLEMENTATION +#include <stb/stb_image.h> + +#include <stb/stb_vorbis.c> + +// #define STB_IMAGE_RESIZE_IMPLEMENTATION +// #include <stb/stb_image_resize2.h> diff --git a/source/stb.h b/source/stb.h new file mode 100644 index 0000000000000000000000000000000000000000..70e9fa1fa9306a369a2735397b02e3910b1ca3ed --- /dev/null +++ b/source/stb.h @@ -0,0 +1,12 @@ +#ifndef YT_STB_H +#define YT_STB_H + +#include <stb/stb_image.h> + +// what the hell stb??? +#define STB_VORBIS_HEADER_ONLY +#include <stb/stb_vorbis.c> + +//#include <stb/stb_image_resize2.h> + +#endif diff --git a/source/texture.c b/source/texture.c new file mode 100644 index 0000000000000000000000000000000000000000..b3561d5374f6ec03bac1c43635628af9cb17e8b0 --- /dev/null +++ b/source/texture.c @@ -0,0 +1,75 @@ +#include "stb.h" +#include "mem.h" +#include "util.h" +#include "texture.h" + +Texture* Texture_LoadMem(uint8_t* img, size_t len) { + int width, height, ch; + + uint8_t* data = stbi_load_from_memory(img, (int) len, &width, &height, &ch, 0); + + if (data == NULL) { + Log("STB error: %s", stbi_failure_reason()); + } + + if (data == NULL) { + Log("Texture_LoadMem: data = NULL"); + return NULL; + } + + int newWidth = width; + int newHeight = height; + + if (width & (width - 1)) { + // i don't know what's going on either + -- newWidth; + newWidth |= newWidth >> 1; + newWidth |= newWidth >> 2; + newWidth |= newWidth >> 4; + newWidth |= newWidth >> 8; + newWidth |= newWidth >> 16; + ++ newWidth; + } + + if (height & (height - 1)) { + -- newHeight; + newHeight |= newHeight >> 1; + newHeight |= newHeight >> 2; + newHeight |= newHeight >> 4; + newHeight |= newHeight >> 8; + newHeight |= newHeight >> 16; + ++ newHeight; + } + + uint8_t* data2 = malloc(newWidth * newHeight * ch); + if (data2 == NULL) { + free(data); + Log("Failed to allocate texture"); + return NULL; + } + + memset(data2, 0, newWidth * newHeight * ch); + + for (int y = 0; y < height; ++ y) { + memcpy(&data2[(y * newWidth * ch)], &data[(y * width * ch)], width * ch); + } + + free(data); + return Backend_LoadTexture(data2, width, height, newWidth, newHeight, ch); +} + +Texture* Texture_LoadFile(const char* path) { + FILE* file = fopen(path, "rb"); + + size_t size; + fseek(file, 0, SEEK_END); + size = (size_t) ftell(file); + fseek(file, 0, SEEK_SET); + + uint8_t* data = SafeMalloc(size); + fread(data, 1, size, file); // TODO: check + + Texture* ret = Texture_LoadMem(data, size); + free(data); + return ret; +} diff --git a/source/texture.h b/source/texture.h new file mode 100644 index 0000000000000000000000000000000000000000..f2227d212533a15f5f3f86980d42b32c689f3723 --- /dev/null +++ b/source/texture.h @@ -0,0 +1,9 @@ +#ifndef YT_TEXTURE_H +#define YT_TEXTURE_H + +#include "backend.h" + +Texture* Texture_LoadMem(uint8_t* img, size_t len); +Texture* Texture_LoadFile(const char* path); + +#endif diff --git a/source/types.c b/source/types.c new file mode 100644 index 0000000000000000000000000000000000000000..29f7509b45fdbcff111ca20be150c99ade12788d --- /dev/null +++ b/source/types.c @@ -0,0 +1,7 @@ +#include "types.h" + +bool PointInRect(Vec2 point, Rect rect) { + return + (point.x >= rect.x) && (point.x < rect.x + rect.w) && + (point.y >= rect.y) && (point.y < rect.y + rect.h); +} diff --git a/source/types.h b/source/types.h new file mode 100644 index 0000000000000000000000000000000000000000..73bbf8e8d6471a20ef4b5eab116782fa6531e3d2 --- /dev/null +++ b/source/types.h @@ -0,0 +1,34 @@ +#ifndef YT_TYPES_H +#define YT_TYPES_H + +#include "common.h" + +typedef struct { + int x, y; +} Vec2; + +typedef struct { + float x, y; +} FVec2; + +typedef struct { + int x, y, w, h; +} Rect; + +typedef struct { + float x, y, w, h; +} FRect; + +typedef struct { + float x, y, z; +} FVec3; + +typedef struct { + float pitch; + float yaw; + float roll; +} Direction; + +bool PointInRect(Vec2 point, Rect rect); + +#endif diff --git a/source/util.c b/source/util.c new file mode 100644 index 0000000000000000000000000000000000000000..fadf402cb1db4cf9f705ed37a0562bfed606f6d8 --- /dev/null +++ b/source/util.c @@ -0,0 +1,197 @@ +#include <stdio.h> +#include <stdarg.h> +#include <time.h> +#include <string.h> +#include "mem.h" +#include "util.h" +#include "video.h" +#include "common.h" + +float RadToDeg(float rad) { + return rad * 180 / PI; +} + +float DegToRad(float deg) { + return deg * (PI / 180); +} + +float Lerp(float a, float b, float t) { + return a * (1.0 - t) + (b * t); +} + +FVec2 LerpVec(FVec2 a, FVec2 b, float t) { + return (FVec2) {Lerp(a.x, b.x, t), Lerp(a.y, b.y, t)}; +} + +float CosDeg(float deg) { + return cos(DegToRad(deg)); +} + +float SinDeg(float deg) { + return sin(DegToRad(deg)); +} + +bool FloatEqual(float a, float b, float margin) { + return fabsf(a - b) < margin; +} + +float Distance(FVec2 a, FVec2 b) { + float x = (b.x - a.x) * (b.x - a.x); + float y = (b.y - a.y) * (b.y - a.y); + return sqrtf(x + y); +} + +float DistanceI(Vec2 a, Vec2 b) { + return Distance( + (FVec2) {(float) a.x, (float) a.y}, (FVec2) {(float) b.x, (float) b.y} + ); +} + +float GetAngle(FVec2 a, FVec2 b) { + return RadToDeg(atan2(b.y - a.y, b.x - a.x)); +} + +#define CROSS_PRODUCT(X1, Y1, X2, Y2) ((X1) * (Y2) - (X2) * (Y1)) + +static double PointCrossProduct(FVec2 a, FVec2 b) { + return CROSS_PRODUCT(a.x, a.y, b.x, b.y); +} + +FVec2 LineIntersect(FVec2 a1, FVec2 b1, FVec2 b2, FVec2 a2) { + double vxp1 = PointCrossProduct(a1, b1); + double vxp2 = PointCrossProduct(a2, b2); + double vxp3 = CROSS_PRODUCT(a1.x - b1.x, a1.y - b1.y, a2.x - b2.x, a2.y - b2.y); + return (FVec2) { + CROSS_PRODUCT(vxp1, a1.x - b1.x, vxp2, a2.x - b2.x) / vxp3, + CROSS_PRODUCT(vxp1, a1.y - b1.y, vxp2, a2.y - b2.y) / vxp3, + }; +} + +float PointLineSide(FVec2 p, FVec2 a, FVec2 b) { + return CROSS_PRODUCT(b.x - a.x, b.y - a.y, p.x - a.x, p.y - a.y); +} + +#define BOUND_MARGIN 0.001 +bool PointInLine(FVec2 p, FVec2 a, FVec2 b) { + if (a.x > b.x) SWAP(float, a.x, b.x); + + if ((p.x < a.x - BOUND_MARGIN) || (p.x > b.x + BOUND_MARGIN)) return false; + + if (a.y > b.y) SWAP(float, a.y, b.y); + + if ((p.y < a.y - BOUND_MARGIN) || (p.y > b.y + BOUND_MARGIN)) return false; + + return true; +} +#undef BOUND_MARGIN + +bool RectLineCollision(FVec2 a, FVec2 b, FRect rect, FVec2* res) { + FVec2 lines[5] = { + {rect.x, rect.y}, {rect.x + rect.w, rect.y}, + {rect.x + rect.w, rect.y + rect.h}, {rect.x, rect.y + rect.h} + }; + lines[4] = lines[0]; + + FVec2 center = {rect.x + (rect.w / 2), rect.y + (rect.h / 2)}; + + for (int i = 0; i < 4; ++ i) { + FVec2 intersection = LineIntersect(lines[i], lines[i + 1], a, b); + + if (PointInLine(intersection, lines[i], lines[i + 1])) { + *res = (FVec2) { + intersection.x - center.x, intersection.y - center.y + }; + return true; + } + } + + return false; +} + +float LinePointDistance(FVec2 la, FVec2 lb, FVec2 point) { + float a = -(lb.y - la.y); + float b = lb.x - la.x; + float c = -(a * la.x) - (b * la.y); + + float dist = (a * point.x) + (b * point.y) + c; + dist = fabsf(dist); + dist /= sqrtf((a * a) + (b * b)); + + return dist; +} + +char* NewString(const char* src) { + char* ret = SafeMalloc(strlen(src) + 1); + strcpy(ret, src); + return ret; +} + +char* ConcatString(const char* first, const char* second) { + char* ret = SafeMalloc(strlen(first) + strlen(second) + 1); + strcpy(ret, first); + strcat(ret, second); + return ret; +} + +size_t StrArrayLength(char** array) { + size_t ret = 0; + + while (*array != NULL) { + ++ ret; + ++ array; + } + + return ret; +} + +char** AppendStrArray(char** array, char* string) { + size_t len = StrArrayLength(array); + + array = SafeRealloc(array, (len + 2) * sizeof(char*)); + array[len] = string; + array[len + 1] = NULL; + return array; +} + +bool StrArrayContains(char** array, char* string) { + while (true) { + if (*array == NULL) { + return false; + } + else if (strcmp(*array, string) == 0) { + return true; + } + + ++ array; + } +} + +size_t StrArrayFind(char** array, char* string) { + for (size_t i = 0; array[i]; ++ i) { + if (strcmp(array[i], string) == 0) { + return i; + } + } + + assert(0); +} + +void FreeStrArray(char** array) { + for (size_t i = 0; array[i] != NULL; ++ i) { + free(array[i]); + } + + free(array); +} + +const char* BaseName(const char* path) { + char* ret = strrchr(path, '/'); + + if (ret) return ret + 1; + + ret = strrchr(path, ':'); + + if (ret) return ret + 1; + + return ret; +} diff --git a/source/util.h b/source/util.h new file mode 100644 index 0000000000000000000000000000000000000000..359c5143f8c851ec23de79ac354f923d14a1ae73 --- /dev/null +++ b/source/util.h @@ -0,0 +1,48 @@ +#ifndef YT_UTIL_H +#define YT_UTIL_H + +#include <math.h> +#include <stdio.h> +#include "log.h" +#include "types.h" + +#define FUNCTION_POINTER(TYPE, NAME, ...) TYPE (*NAME)(__VA_ARGS__) +#define MIN(A, B) (((A) < (B))? (A) : (B)) +#define MAX(A, B) (((A) > (B))? (A) : (B)) + +#define SWAP(TYPE, A, B) \ + do { \ + TYPE* macro##a = &(A); \ + TYPE* macro##b = &(B); \ + TYPE macro##tmp = *macro##a; \ + *macro##a = *macro##b; \ + *macro##b = macro##tmp; \ + } while (0); + +bool FileExists(const char* path); +float RadToDeg(float rad); +float DegToRad(float deg); +float Lerp(float a, float b, float t); +FVec2 LerpVec(FVec2 a, FVec2 b, float t); +float CosDeg(float deg); +float SinDeg(float deg); +bool FloatEqual(float a, float b, float margin); +float Distance(FVec2 a, FVec2 b); +float DistanceI(Vec2 a, Vec2 b); +float GetAngle(FVec2 a, FVec2 b); +float PointLineSide(FVec2 p, FVec2 a, FVec2 b); +FVec2 LineIntersect(FVec2 a1, FVec2 a2, FVec2 b1, FVec2 b2); +bool PointInLine(FVec2 p, FVec2 a, FVec2 b); +bool RectLineCollision(FVec2 a, FVec2 b, FRect rect, FVec2* res); +float LinePointDistance(FVec2 la, FVec2 lb, FVec2 point); +char* NewString(const char* src); +char* ConcatString(const char* first, const char* second); +size_t StrArrayLength(char** array); +char** AppendStrArray(char** array, char* string); +bool StrArrayContains(char** array, char* string); +size_t StrArrayFind(char** array, char* string); +void FreeStrArray(char** array); + +const char* BaseName(const char* path); + +#endif diff --git a/source/video.c b/source/video.c new file mode 100644 index 0000000000000000000000000000000000000000..b3595cafb47ea9807b7caeec0d31ff66ab7ee5be --- /dev/null +++ b/source/video.c @@ -0,0 +1,71 @@ +#include "util.h" +#include "event.h" +#include "video.h" +#include "window.h" +#include "backend.h" + +Video video; + +static void EventHandler(Event* e) { + Window* win = &video.windows[e->windowResize.window]; // TODO: check + + win->aWidth = e->windowResize.width; + win->aHeight = e->windowResize.height; + + win->width = win->aWidth; + win->height = win->aHeight; +} + +void Video_Init(const char* windowName) { + Backend_Init(true); + + // options + video.fog = false; + + int w; + int h; + + w = 640; + h = 480; + + video.windows[0] = Window_Create(windowName, w, h); + video.windows[0].width = w; + video.windows[0].height = h; + Log("Created window 0"); + + Backend_Init(false); + + Event_AddHandler(EVENT_WINDOW_RESIZE, &EventHandler); +} + +void Video_Free(void) { + Backend_Free(); + for (int i = 0; i < WIN_NUM; ++ i) { + Window_Free(&video.windows[i]); + } +} + +Colour Video_MultiplyColour(Colour colour, float by) { + float r = ((float) colour.r) / 255.0; + float g = ((float) colour.g) / 255.0; + float b = ((float) colour.b) / 255.0; + + r *= by; + g *= by; + b *= by; + + if (r > 1.0) r = 1.0; + if (g > 1.0) g = 1.0; + if (b > 1.0) b = 1.0; + + if (r < 0.0) r = 0.0; + if (g < 0.0) g = 0.0; + if (b < 0.0) b = 0.0; + + return (Colour) { + (uint8_t) (r * 255.0), + (uint8_t) (g * 255.0), + (uint8_t) (b * 255.0), + colour.a + }; +} diff --git a/source/video.h b/source/video.h new file mode 100644 index 0000000000000000000000000000000000000000..17618f53bdf71f0b5a2d0fdb908d62488a8dffa5 --- /dev/null +++ b/source/video.h @@ -0,0 +1,27 @@ +#ifndef YT_VIDEO_H +#define YT_VIDEO_H + +#include "common.h" +#include "window.h" + +#undef main + +#define WIN_NUM 1 + +typedef struct { + uint8_t r, g, b, a; +} Colour; + + +typedef struct { + Window windows[WIN_NUM]; + bool fog; +} Video; + +extern Video video; + +void Video_Init(const char* windowName); +void Video_Free(void); +Colour Video_MultiplyColour(Colour colour, float by); + +#endif diff --git a/source/window/sdl2.c b/source/window/sdl2.c new file mode 100644 index 0000000000000000000000000000000000000000..9915b692813c27577b32273d9aec1d558fed8433 --- /dev/null +++ b/source/window/sdl2.c @@ -0,0 +1,72 @@ +#include "../util.h" +#include "../window.h" + +#ifdef YT_WINDOW_SDL2 +#include "sdl2.h" + +const bool windowRequired = true; + +static bool cursorVisible = true; +static bool relativeMouseMode = false; + +void Window_Init(void) { + if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { + Log("Failed to initialise SDL2: %s", SDL_GetError()); + exit(1); + } +} + +void Window_Quit(void) { + SDL_Quit(); +} + +Window Window_Create(const char* name, int width, int height) { + Window ret; + ret.window = SDL_CreateWindow( + name, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, + SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE + ); + ret.aWidth = width; + ret.aHeight = height; + ret.width = width; + ret.height = height; + + if (ret.window == NULL) { + Log("Failed to create window: %s", SDL_GetError()); + exit(1); + } + + return ret; +} + +void Window_Free(Window* window) { + SDL_DestroyWindow(window->window); +} + +void Window_ShowCursor(bool show) { + if (cursorVisible != show) { + SDL_ShowCursor(show? SDL_ENABLE : SDL_DISABLE); + } + + cursorVisible = show; +} + +void Window_SetRelativeMouseMode(bool enable) { + if (relativeMouseMode != enable) { + SDL_SetRelativeMouseMode(enable? SDL_TRUE : SDL_FALSE); + } + + relativeMouseMode = enable; +} + +void Window_MessageBox(int type, const char* title, const char* contents) { + uint32_t flags; + switch (type) { + case WINDOW_MSG_WARNING: flags = SDL_MESSAGEBOX_WARNING; break; + case WINDOW_MSG_ERROR: flags = SDL_MESSAGEBOX_ERROR; break; + } + + SDL_ShowSimpleMessageBox(flags, title, contents, NULL); +} + +#endif diff --git a/source/window/sdl2.h b/source/window/sdl2.h new file mode 100644 index 0000000000000000000000000000000000000000..0ae0a0630016ec5d954ab9e235747ef3cfad1c1a --- /dev/null +++ b/source/window/sdl2.h @@ -0,0 +1,12 @@ +#ifndef YT_WINDOW_SDL2_H +#define YT_WINDOW_SDL2_H + +#include <SDL2/SDL.h> + +typedef struct { + WINDOW_COMMON; + + SDL_Window* window; +} Window; + +#endif diff --git a/source/window.h b/source/window.h new file mode 100644 index 0000000000000000000000000000000000000000..61094ff8edcd0d1d6e14df8e1233ab4fc626fe11 --- /dev/null +++ b/source/window.h @@ -0,0 +1,29 @@ +#ifndef YT_WINDOW_H +#define YT_WINDOW_H + +#include "common.h" + +#define WINDOW_COMMON \ + int width, height; \ + int aWidth, aHeight + +#ifdef YT_WINDOW_SDL2 + #include "window/sdl2.h" +#elif defined(PLATFORM_3DS) + #include "window/ae3ds.h" +#endif + +enum { + WINDOW_MSG_WARNING = 0, + WINDOW_MSG_ERROR +}; + +void Window_Init(void); +void Window_Quit(void); +Window Window_Create(const char* name, int width, int height); +void Window_Free(Window* window); +void Window_ShowCursor(bool show); +void Window_SetRelativeMouseMode(bool enable); +void Window_MessageBox(int type, const char* title, const char* contents); + +#endif