diff --git a/.gitignore b/.gitignore index dd86103..12b1da2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *.o local/ build/ -demo/demo +demo/2d_collision/2d_collision +demo/squircle/squircle +demo/cube/cube diff --git a/README b/README index f1d010c..fc4f2e8 100644 --- a/README +++ b/README @@ -1,26 +1,37 @@ -++~~~~~~~~~~~~~~~~~~~~~~~++ -++~~~~~~~~~~~~~~~~~~~~~~~++ -:: :: -:: SFW (SDL Framework) :: -:: :: -++~~~~~~~~~~~~~~~~~~~~~~~++ -++~~~~~~~~~~~~~~~~~~~~~~~++ + /\ +--------------------------------------------------------------+ + ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | + \ / / | copy, modify and sell without restriction | + +--\ ^__^ /--+ | | + | ~/ \~ | | - originally created at [http://nugget.fun] | + | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ + | SPACE ~~~~~ | / + | ~~~~~~~ BOX |/ + +--------------+ + +[SPACE BOX] is a C++ framework that facilitates the creation of SDL + OpenGL projects +through the use of generic objects that can be used and extended by the programmer. It +focuses on game projects but its usefulness is not limited to games. -SFW is a C++ framework that facilitates the creation of SDL projects. It -provides generic game objects that can be used, extended or overwritten. Games -written using this framework can be exported to PC, Mac, Linux, Web GL, -Raspberry Pi, and Android. +The main intention for the project is to make running cross-platform applications easier +and more immediate by providing a universal tool set that exists as a hidden, extensible +layer between SDL + OpenGL and the project. -It is in an early untested state. It comes with a simple program that -demonstrates how to use it for a project that can switch between SDL and OpenGL -contexts. +An important quality of the framework is it should allow the programmer to start a project +by extending only a single function, the Game class's update function. Additionally, the +framework should by default create applications that can be easily exported to many +platforms (PC, OS/X, Linux, Web GL, Raspberry Pi, and Android) by using a standard +Makefile. -Requirements -```````````` -The SFW source includes some external libraries in lib/ that the default -Makefile included with the demo shows how to compile, but there are other -libraries that must also be present in order to compile a project which uses the -framework +It is in an early, untested state. It comes with a few simple examples that demonstrate +how in can be used to create interactive graphics programs. + +################ +# Requirements # +################ + +The repository includes some external libraries in lib/ that the default Makefile included +with the demo shows how to compile, but there are other requirements, including external +libraries that must be linked to a project in order to compile it. * libSDL2 (developed against v2.0.14) * libSDL2-image @@ -29,22 +40,23 @@ framework * OpenGL/GLES/GLES2 * compiler that supports C++17 -Installing Requirements -``````````````````````` -libSDL2, libSDL2-image, libSDL2-ttf, and libSDL2-mixer must be available to -link with your project, so you can try your package manager's libSDL2 dev -packages or build from source. The included sdl2-config utility program can be -used to generate flags for linking to SDL2 when it is installed outside of -your platform's usual library location. +########################### +# Installing Requirements # +########################### -libSDL2: +libSDL2, libSDL2-image, libSDL2-ttf, and libSDL2-mixer must be available to link with your +project, so you can try your package manager's libSDL2 dev packages or build from source. +The included sdl2-config utility program can be used to generate flags for linking to SDL2 +when it is installed outside of your platform's usual library location. +libSDL2 +``````` - Download from http://libsdl.org/download-2.0.php - Run ./configure --prefix=[YOUR LIBRARIES PATH] (I'm using $HOME/local/sdl) - Run make && make install -libSDL2-image, libSDL2-ttf, libSDL2-mixer: - +libSDL2-image, libSDL2-ttf, libSDL2-mixer +````````````````````````````````````````` - Download from: - https://www.libsdl.org/projects/SDL_image/ - https://www.libsdl.org/projects/SDL_ttf/ @@ -52,48 +64,80 @@ libSDL2-image, libSDL2-ttf, libSDL2-mixer: - Run ./configure --prefix=[YOUR LIB PATH] --with-sdl-prefix=[YOUR SDL PATH] - In my case, prefix and SDL prefix are both $HOME/local/sdl -OpenGL/GLES/GLES2: +OpenGL/GLES/GLES2 +````````````````` +- Install GL/GLES according to your platform and link to it during compilation. GLEW is + included in the lib/ folder of this framework and should find GL on your platform if it + is installed. -- Install GL/GLES according to your platform and link to it during compilation. - GLEW is included in the lib/ folder of this framework and should find GL on - your platform if it is installed. +######### +# Demos # +######### -Demo +The `demo/` folder contains programs that demonstrate and test the capabilities of the +framework. In order to compile each, you should edit the definitions in the Makefile. + +cube ```` -The `demo/` folder contains a simple cube demo. The main purpose of the demo is -to demonstrate switching between 3D and 2D contexts. In order to compile the -demo, you will have to edit the paths in the Makefile to point to the locations -of the necessary libraries on your system. +Switch between GL and SDL contexts by pressing spacebar. The GL context draws a textured, +rotating cube, and the SDL context draws basic geometric shapes and a moving 2D sprite. -Other libraries -``````````````` -These are other libraries that have been used in projects that use this -framework but aren't required by the framework +2d_collision +```````````` +Test collision detection between a 2D sprite and other 2D sprites and boxes. Per-pixel +collision can be tested. -opencv: +squircle +```````` +Map an image from a rectangle to a circle or from a circle to a rectangle using a shader +program. Based on the elliptical grid mapping equations at: + - http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html + +################### +# Other libraries # +################### + +These are other libraries that have been used in projects that use this framework but +aren't required by the framework + +opencv +`````` - Download from https://opencv.org/releases/ - configure (with custom installation path) cmake -DCMAKE_INSTALL_PREFIX=$HOME/local/opencv .. - make && make install -zbar: - +zbar +```` - Download from http://zbar.sourceforge.net/download.html - configure to only use image processing features (requires imagemagick) and choose your installation directory ./configure --without-gtk --without-python --without-qt --disable-video --prefix=$HOME/local/zbar - make && make install -License -``````` -The original code for this framework is licensed to freely use, copy, modify and -sell, without restriction under the zlib license. See LICENSE.txt for details. +######## +# Font # +######## + +When initializing a Game object, the framework will attempt to load the font file +"BPmono.ttf" from the project root (where the compiled executable is located). If this +file isn't found, the program can still run successfully, but the framerate indicator +(mapped to CTRL+f by default) will be disabled. The repository contains "BPmono.ttf", so +you can create a symlink to the file in the project root if you want to use the framerate +indicator. + +########### +# License # +########### + +The original code for this framework is licensed to freely use, copy, modify and sell, +without restriction under the zlib license. See LICENSE.txt for details. Included libraries are included under various permissive licenses: -- BPmono.ttf is licensed under the Creative Commons Attribution - No Derivative - Works 3.0 license. See LICENSE_BPmono.txt for details. +- BPmono.ttf is licensed under the Creative Commons Attribution - No Derivative Works 3.0 + license. See LICENSE_BPmono.txt for details. - gif-h is unlicensed, public domain code released under the The Unlicense. See lib/gif-h/LICENSE @@ -102,14 +146,14 @@ Included libraries are included under various permissive licenses: - GLM is included under the MIT license in lib/glm/LICENSE -- nlohmann's json library is included under the MIT license in - lib/json/LICENSE.MIT +- nlohmann's json library is included under the MIT license in lib/json/LICENSE.MIT - SDL2 GFX is included under a permissive license in lib/sdl2-gfx/LICENSE -- superxbr.cpp is included under a permissive license at the top of - lib/superxbr.cpp +- superxbr.cpp is included under the permissive license at the top of lib/superxbr.cpp -Business -```````` -420@shampoo.ooo +############ +# Business # +############ + +egg@shampoo.ooo diff --git a/demo/2d_collision/BPmono.ttf b/demo/2d_collision/BPmono.ttf new file mode 120000 index 0000000..80db3f8 --- /dev/null +++ b/demo/2d_collision/BPmono.ttf @@ -0,0 +1 @@ +../../BPmono.ttf \ No newline at end of file diff --git a/demo/2d_collision/CollisionTest.cpp b/demo/2d_collision/CollisionTest.cpp new file mode 100644 index 0000000..d84d847 --- /dev/null +++ b/demo/2d_collision/CollisionTest.cpp @@ -0,0 +1,173 @@ +#include "CollisionTest.hpp" + +CollisionTest::CollisionTest() : Game() +{ + get_delegate().subscribe(&CollisionTest::respond, this); + load_sdl_context(); + enemy.set_color_mod({255, 128, 128, 255}); + wooper.load(); + enemy.load(); + wooper.add_box({80, 200}); + enemy.add_box({90, 100}); + wooper.add_box({300, 10}); + enemy.add_box({90, 200}); + wooper.add_box({180, 20}); + enemy.add_box({180, 50}); + enemy.move({50, 50}); + Pixels wooper_pixels = Pixels(wooper); + for (int x = 0; x < wooper.get_w(); x++) + { + wooper_pixels.set(Color(128, 255, 64), x, 12); + } + wooper_pixels.apply(); + Box box = {{5, 10}, {20, 4}}; + Pixels enemy_pixels = Pixels(enemy, box); + for (int x = -1, y = -1; x > -box.width(); x--, y--) + { + enemy_pixels.set(Color(-1.3, 68.9, 800.8), x, y); + } + enemy_pixels.apply(); + canvas = SDL_CreateTexture(get_renderer(), SDL_PIXELFORMAT_RGBA4444, SDL_TEXTUREACCESS_STREAMING, 200, 100); + Pixels canvas_pixels = Pixels(get_renderer(), canvas); + for (int x = 0, y = 0; x < 288; x++, y += x) + { + canvas_pixels.set(Color(x, y, x + y), x, y); + } + canvas_pixels.apply(); + Box subsection = Box({5, 10}, {60, 20}); + Pixels sub_canvas_pixels = Pixels(get_renderer(), canvas, subsection); + for (int x = 0; x < subsection.width(); x++) + { + for (int y = 0; y < subsection.height(); y++) + { + // *canvas_pixels.operator()(x, y) = 0xfb60; + *canvas_pixels.operator()(x, y) = Color(255, 200, 100); + } + } + SDL_Color color = {0, 1, 2, 3}; + std::cout << std::boolalpha << (Color(255, 255, 255) == Color(255, 255, 255)) << " " << + (Color(122.1, 853.8, -1.3) == Color(122, 86, 255)) << " " << (Color(1, 2, 3, 4) == ((SDL_Color){1, 2, 3, 4})) << " " << + (Color(256, 257, 258, 259) != color) << std::endl; + sub_canvas_pixels.apply(); + test_crop( + { + Box({-5, -2}, {10, 5}), + Box({window_box().right() - 30, 10}, {100, 1000}), + Box(window_box().se() - glm::vec2(10, 15), {789, 123}), + Box(window_box().sw() - glm::vec2(1, 1), {5, 5}) + }); + // std::cout << "out of bounds pixel is " << screen_subsection_pixels.get(0, 0) << + // " out of bounds pixel is " << screen_subsection_pixels.get(-1, 0) << + // " in bounds pixel is " << screen_subsection_pixels.get(0, -1) << + // " out of bounds pixel is " << screen_subsection_pixels.get(-1, -1) << std::endl; + // screen_subsection_pixels.set(Color(255, 0, 0), 0, -1); + // screen_subsection_pixels.set(Color(255, 255, 255), 0, 0); + // screen_subsection_pixels.apply(); +} + +void CollisionTest::test_crop(const std::vector& boxes) +{ + for (const Box& box : boxes) + { + Pixels screen_subsection_pixels = Pixels(get_renderer(), nullptr, box); + std::cout << box << " cropped by screen to " << screen_subsection_pixels.rect << std::endl; + } +} + +void CollisionTest::respond(SDL_Event& event) +{ + if (get_delegate().compare(event, "up")) + { + wooper.move_weighted({0, -2}); + } + else if (get_delegate().compare(event, "left")) + { + wooper.move_weighted({-2, 0}); + } + else if (get_delegate().compare(event, "right")) + { + wooper.move_weighted({2, 0}); + } + else if (get_delegate().compare(event, "down")) + { + wooper.move_weighted({0, 2}); + } + else if (get_delegate().compare(event, "toggle-collide-all")) + { + collide_all = !collide_all; + } + else if (get_delegate().compare(event, "toggle-collide-all-other")) + { + collide_all_other = !collide_all_other; + } + else if (get_delegate().compare(event, "toggle-precise-collision")) + { + precise = !precise; + } +} + +void CollisionTest::update() +{ + SDL_SetRenderTarget(get_renderer(), NULL); + SDL_SetRenderDrawColor(get_renderer(), 64, 128, 64, 255); + SDL_RenderClear(get_renderer()); + wooper.update(); + enemy.update(); + SDL_Color white = {255, 255, 255, 255}, red = {255, 0, 0, 255}, color; + Box overlap; + SDL_Rect rect; + for (const Box& box : boxes) + { + color = wooper.collide(box, overlap, precise, collide_all) ? red : white; + SDL_SetRenderTarget(get_renderer(), NULL); + SDL_SetRenderDrawColor(get_renderer(), color.r, color.g, color.b, color.a); + rect = box; + SDL_RenderDrawRect(get_renderer(), &rect); + rect = overlap; + SDL_SetRenderDrawColor(get_renderer(), 255, 255, 0, 255); + SDL_RenderDrawRect(get_renderer(), &rect); + } + glm::vec2 intersection; + for (const Segment& segment : segments) + { + color = wooper.collide(segment, intersection, collide_all) ? red : white; + glm::vec2 start = segment.start(), end = segment.end(); + SDL_SetRenderDrawColor(get_renderer(), color.r, color.g, color.b, color.a); + SDL_RenderDrawLine(get_renderer(), start.x, start.y, end.x, end.y); + SDL_SetRenderDrawColor(get_renderer(), 255, 255, 0, 255); + SDL_RenderDrawPoint(get_renderer(), intersection.x, intersection.y); + } + for (const glm::vec2& point : points) + { + color = wooper.collide(point, collide_all) ? red : white; + SDL_SetRenderDrawColor(get_renderer(), color.r, color.g, color.b, color.a); + SDL_RenderDrawPoint(get_renderer(), point.x, point.y); + } + if (wooper.collide(enemy, overlap, precise, collide_all, collide_all_other)) + { + SDL_SetRenderTarget(get_renderer(), nullptr); + SDL_SetRenderDrawColor(get_renderer(), 0, 255, 0, 255); + rect = overlap; + SDL_RenderDrawRect(get_renderer(), &rect); + } + Box screen_subsection_box = Box({0, 0}, {30, 60}); + screen_subsection_box.sw(window_box().sw()); + Pixels screen_subsection_pixels = Pixels(get_renderer(), nullptr, screen_subsection_box); + for (int x = 0; x < screen_subsection_pixels.rect.w; x++) + { + screen_subsection_pixels.set(Color(255, 0, 255), x, 5); + } + screen_subsection_pixels.apply(); + SDL_SetRenderTarget(get_renderer(), nullptr); + Box canvas_box = sfw::get_texture_box(canvas); + canvas_box.nw({340, 200}); + SDL_RenderCopyF(get_renderer(), canvas, nullptr, &canvas_box); +} + +int main() +{ + CollisionTest collision_test = CollisionTest(); + collision_test.run(); + collision_test.quit(); + return 0; +} diff --git a/demo/2d_collision/CollisionTest.hpp b/demo/2d_collision/CollisionTest.hpp new file mode 100644 index 0000000..4d255ff --- /dev/null +++ b/demo/2d_collision/CollisionTest.hpp @@ -0,0 +1,42 @@ +#ifndef CollisionTest_h_ +#define CollisionTest_h_ + +#include "Game.hpp" +#include "Sprite.hpp" +#include "Pixels.hpp" +#include "Color.hpp" +#include "extension.hpp" + +class CollisionTest : public Game +{ + +private: + + Sprite wooper = Sprite(this, "wooper.png"); + Sprite enemy = Sprite(this, "wooper.png"); + SDL_Texture* canvas; + std::vector boxes = { + {{50, 20}, {20, 10}}, + {{300, 100}, {50, 60}}, + {{160, 300}, {30, 10}}, + }; + std::vector segments = { + {{5, 50}, {15, 90}}, + {{250, 250}, {200, 240}} + }; + std::vector points = {{100, 300}, {205, 20}, {450, 300}}; + bool collide_all = false; + bool collide_all_other = false; + bool precise = false; + + void test_crop(const std::vector&); + void respond(SDL_Event&); + void update(); + +public: + + CollisionTest(); + +}; + +#endif diff --git a/demo/2d_collision/Makefile b/demo/2d_collision/Makefile new file mode 100644 index 0000000..5d0ed98 --- /dev/null +++ b/demo/2d_collision/Makefile @@ -0,0 +1,101 @@ +# /\ +--------------------------------------------------------------+ +# ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | +# \ / / | copy, modify and sell without restriction | +# +--\ ^__^ /--+ | | +# | ~/ \~ | | - originally created at [http://nugget.fun] | +# | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ +# | SPACE ~~~~~ | / +# | ~~~~~~~ BOX |/ +# +--------------+ +# +# [ Makefile for 2D collision demo ] +# +# This should build the 2D collision demo for Linux. Compilation to other platforms +# hasn't been attempted yet. +# +# Edit the parameters as necessary and run `make linux` in the current directory. +# + +####################### +# Location parameters # +####################### + +# Location of source files for the demo +SRC_DIR := ./ + +# Locations of [SPACE BOX] source and dependencies required to be compiled from source. These +# locations are configured to match the structure of the [SPACE BOX] repository but can be +# modified as necessary. +SB_DIR := ../../ +SB_SRC_DIR := $(SB_DIR)src/ +SB_LIB_DIR := $(SB_DIR)lib/ +SDLGFX2_DIR := $(SB_LIB_DIR)sdl2-gfx/ +GLEW_DIR := $(SB_LIB_DIR)glew/ + +# C and C++ compiler commands +CC := clang +CPPC := clang++ + +# Location of SDL config program +SDLCONFIG := ~/local/sdl/bin/sdl2-config + +# Edit to point to the location of BPmono.ttf +CREATE_FONT_SYMLINK := ln -nsf $(SB_DIR)"BPmono.ttf" . + +############################# +# Based on above parameters # +############################# + +CFLAGS := -Wall -O0 -c -I$(SB_LIB_DIR) -I$(SB_SRC_DIR) -g $(shell $(SDLCONFIG) --cflags) +CPP_FLAGS := $(CFLAGS) --std=c++17 +LFLAGS := $(shell $(SDLCONFIG) --libs) -lpthread +SB_H_FILES := $(wildcard $(addprefix $(SB_SRC_DIR),*.hpp)) +SB_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o),$(SB_H_FILES:.hpp=.o)) +SRC_H_FILES := $(wildcard $(addprefix $(SRC_DIR),*.hpp)) +SRC_O_FILES := $(SRC_H_FILES:.hpp=.o) + +################################################################## +# Targets for building [SPACE BOX], dependencies and demo source # +################################################################## + +$(SDLGFX2_DIR)%.o: $(SDLGFX2_DIR)%.c $(SDLGFX2_DIR)%.h +$(GLEW_DIR)%.o: $(GLEW_DIR)%.c $(GLEW_DIR)%.h + $(CC) $(CFLAGS) $< -c -o $@ + +$(SB_SRC_DIR)extension.o : $(addprefix $(SB_SRC_DIR),Box.hpp Segment.hpp Color.hpp filesystem.hpp Pixels.hpp) +$(SB_SRC_DIR)Node.o : $(addprefix $(SB_SRC_DIR),Game.hpp Configuration.hpp Delegate.hpp Display.hpp Input.hpp Box.hpp Audio.hpp) +$(SB_SRC_DIR)Sprite.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Animation.hpp Color.hpp extension.hpp Pixels.hpp) +$(SB_SRC_DIR)Game.o : $(addprefix $(SB_SRC_DIR),extension.hpp Node.hpp Sprite.hpp Recorder.hpp Input.hpp Configuration.hpp \ + Delegate.hpp Audio.hpp) +$(SB_SRC_DIR)Animation.o : $(addprefix $(SB_SRC_DIR),Node.hpp Timer.hpp) +$(SB_SRC_DIR)Recorder.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Configuration.hpp Delegate.hpp Animation.hpp extension.hpp) +$(SB_SRC_DIR)Input.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp Configuration.hpp Delegate.hpp) +$(SB_SRC_DIR)Configuration.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp) +$(SB_SRC_DIR)Delegate.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Input.hpp) +$(SB_SRC_DIR)Display.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Configuration.hpp Delegate.hpp) +$(SB_SRC_DIR)Box.o : $(addprefix $(SB_SRC_DIR),extension.hpp Segment.hpp) +$(SB_SRC_DIR)Segment.o : $(addprefix $(SB_SRC_DIR),extension.hpp Box.hpp) +$(SB_SRC_DIR)Pixels.o : $(addprefix $(SB_SRC_DIR),Box.hpp extension.hpp) +$(SB_SRC_DIR)Audio.o : $(addprefix $(SB_SRC_DIR),Node.hpp Display.hpp Configuration.hpp Box.hpp filesystem.hpp extension.hpp) +$(SRC_DIR)CollisionTest.o : $(SB_H_FILES) +%.o : %.cpp %.hpp + $(CPPC) $(CPP_FLAGS) $< -c -o $@ + +########################## +# Target for Linux build # +########################## + +linux : $(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) \ + $(SB_O_FILES) CollisionTest.o + $(CREATE_FONT_SYMLINK) + $(CPPC) $(LFLAGS) -D__LINUX__ -lGL -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs $^ -o 2d_collision + +####################################### +# Target for cleaning up object files # +####################################### + +clean : + - rm *.o + - rm $(SB_SRC_DIR)*.o + - rm $(GLEW_DIR)*.o + - rm $(SDLGFX2_DIR)*.o diff --git a/demo/2d_collision/config.json b/demo/2d_collision/config.json new file mode 100644 index 0000000..ec129ec --- /dev/null +++ b/demo/2d_collision/config.json @@ -0,0 +1,8 @@ +{ + "keys": + { + "toggle-collide-all": ["CTRL", "c"], + "toggle-collide-all-other": ["CTRL", "o"], + "toggle-precise-collision": ["CTRL", "p"] + } +} diff --git a/demo/2d_collision/wooper.png b/demo/2d_collision/wooper.png new file mode 100644 index 0000000..1d70c2e Binary files /dev/null and b/demo/2d_collision/wooper.png differ diff --git a/demo/Makefile b/demo/Makefile deleted file mode 100644 index 3257271..0000000 --- a/demo/Makefile +++ /dev/null @@ -1,120 +0,0 @@ -SFW_DIR = ../ -SFW_SRC_DIR = $(SFW_DIR)src/ -SFW_LIB_DIR = $(SFW_DIR)lib/ -SDLGFX2_DIR = $(SFW_LIB_DIR)sdl2-gfx/ -GLEW_DIR = $(SFW_LIB_DIR)glew/ -CC_LINUX = clang-7 -CPPC_LINUX = clang++-7 -SDLCONFIG = /home/frank/local/sdl/bin/sdl2-config -CFLAGS = -Wall -O0 -c -I$(SFW_LIB_DIR) -I$(SFW_SRC_DIR) -g -CPP_FLAGS = $(CFLAGS) --std=c++17 -SDL_FLAGS = $(shell $(SDLCONFIG) --cflags) -LFLAGS = $(shell $(SDLCONFIG) --libs) -lpthread -export ANDROID_HOME = /home/frank/ext/software/android-sdk -export ANDROID_NDK_HOME = /home/frank/ext/software/android-ndk-r8d -BUILDDIR = build -ANDROIDPROJECT = com.tarecontrol.demo -SOFTWARE_ROOT = /home/frank/ext/software -SDLHOME = $(SOFTWARE_ROOT)/SDL2-2.0.9 -SDL_IMG_HOME = $(SOFTWARE_ROOT)/SDL2_image-2.0.4 -SDL_TTF_HOME = $(SOFTWARE_ROOT)/SDL2_ttf-2.0.14 -GLEW_WIN32_HOME = $(SOFTWARE_ROOT)/glew-2.1.0-win32 -PROJECTHOME = $(shell pwd) -EMSCRIPTENHOME = /home/frank/ext/software/emsdk/emscripten/tag-1.38.12 -SDLEMLIBSHOME = $(SDLHOME)/build/em/build/.libs -EMBUILDDIR = em -WINBUILDDIR = win -SDLMINGWHOME = $(SDLHOME)/i686-w64-mingw32 -SDL_IMG_MINGW_HOME = $(SDL_IMG_HOME)/i686-w64-mingw32 -SDL_TTF_MINGW_HOME = $(SDL_TTF_HOME)/i686-w64-mingw32 -APPDIR = Main.app/Contents -SYSFWPATH = /Library/Frameworks - -$(SDLGFX2_DIR)%.o: $(SDLGFX2_DIR)%.c $(SDLGFX2_DIR)%.h - $(CC_LINUX) $(CFLAGS) $(SDL_FLAGS) $< -o $@ - -$(GLEW_DIR)%.o: $(GLEW_DIR)%.c $(GLEW_DIR)%.h - $(CC_LINUX) $(CFLAGS) $< -o $@ - -$(SFW_SRC_DIR)Sprite.o: $(addprefix $(SFW_SRC_DIR),Game.*pp Box.*pp Node.*pp Animation.*pp) -$(SFW_SRC_DIR)Game.o: $(addprefix $(SFW_SRC_DIR),Sprite.*pp Configuration.*pp Delegate.*pp Display.*pp \ - Recorder.*pp Node.*pp Input.*pp) -$(SFW_SRC_DIR)Node.o: $(addprefix $(SFW_SRC_DIR),Game.*pp Configuration.*pp Delegate.*pp) -$(SFW_SRC_DIR)Animation.o: $(addprefix $(SFW_SRC_DIR),Node.*pp Timer.*pp) -$(SFW_SRC_DIR)Recorder.o: $(addprefix $(SFW_SRC_DIR),extension.*pp Node.*pp Delegate.*pp Animation.*pp Display.*pp) -$(SFW_SRC_DIR)Input.o: $(addprefix $(SFW_SRC_DIR),Delegate.*pp Node.*pp) -$(SFW_SRC_DIR)Configuration.o: $(addprefix $(SFW_SRC_DIR),Node.*pp) -$(SFW_SRC_DIR)Delegate.o: $(addprefix $(SFW_SRC_DIR),Node.*pp) -$(SFW_SRC_DIR)Display.o: $(addprefix $(SFW_SRC_DIR),Node.*pp) -$(SFW_SRC_DIR)%.o: $(addprefix $(SFW_SRC_DIR),%.cpp %.hpp) - $(CPPC_LINUX) $(CPP_FLAGS) $(SDL_FLAGS) $< -o $@ - -Demo.o: Demo.cpp Demo.hpp $(addprefix $(SFW_SRC_DIR),Sprite.*pp Node.*pp Game.*pp Box.*pp Input.*pp \ - Recorder.*pp Timer.*pp Animation.*pp extension.*pp) - $(CPPC_LINUX) $(CPP_FLAGS) $(SDL_FLAGS) $< -o $@ - -linux: Demo.o $(addprefix $(SFW_SRC_DIR),Sprite.o Node.o Game.o Box.o Configuration.o Input.o Delegate.o \ - Display.o Recorder.o Timer.o Animation.o extension.o) \ - $(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) - $(CPPC_LINUX) $(LFLAGS) -D__LINUX__ $^ -lGL -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs -o demo - -clean: - rm *.o - rm $(SFW_SRC_DIR)*.o - rm $(GLEW_DIR)*.o - rm $(SDLGFX2_DIR)*.o - -android : - if [ ! -d $(BUILDDIR) ]; then mkdir $(BUILDDIR); fi; - cd $(SDLHOME)/build-scripts/ && \ - ./androidbuild.sh $(ANDROIDPROJECT) $(PROJECTHOME)/main.cpp - cp -r $(SDLHOME)/build/$(ANDROIDPROJECT) $(BUILDDIR) - sed -i s/2\.3\.3/2\.2\.3/g $(BUILDDIR)/$(ANDROIDPROJECT)/build.gradle - sed -i s/26\.0\.1/23\.0\.1/g $(BUILDDIR)/$(ANDROIDPROJECT)/app/build.gradle - cd $(BUILDDIR)/$(ANDROIDPROJECT) && ./gradlew assembleDebug - -emscripten : - if [ ! -d $(BUILDDIR)/$(EMBUILDDIR) ]; then mkdir -p $(BUILDDIR)/$(EMBUILDDIR); fi; - cd $(BUILDDIR)/$(EMBUILDDIR) && \ - $(EMSCRIPTENHOME)/em++ -O2 $(PROJECTHOME)/main.cpp -I$(SDLHOME)/include \ - -Wall -s USE_SDL=2 -o main.html - -mingw : - if [ ! -d $(BUILDDIR)/$(WINBUILDDIR) ]; then mkdir -p $(BUILDDIR)/$(WINBUILDDIR); fi; - cd $(BUILDDIR)/$(WINBUILDDIR) && \ - i686-w64-mingw32-g++ -std=c++17 $(PROJECTHOME)/main.cpp -I$(SDLMINGWHOME)/include/SDL2 \ - -I$(SDL_IMG_MINGW_HOME)/include/SDL2 -I$(SDL_TTF_MINGW_HOME)/include/SDL2 $(INC) \ - $(PROJECTHOME)/sdl2-gfx/SDL2_gfxPrimitives.c $(PROJECTHOME)/sdl2-gfx/SDL2_rotozoom.c $(PROJECTHOME)/glew/glew.c \ - -L$(SDLMINGWHOME)/lib -L$(SDL_IMG_MINGW_HOME)/lib -L$(SDL_TTF_MINGW_HOME)/lib \ - -lmingw32 -lSDL2_image \ - -lSDL2_ttf -lstdc++fs \ - -lSDL2main -lSDL2 -lopengl32 -Wall -O2 -o main.exe && \ - cp $(SDLMINGWHOME)/bin/SDL2.dll $(SDL_IMG_MINGW_HOME)/bin/SDL2_image.dll \ - $(SDL_TTF_MINGW_HOME)/bin/SDL2_ttf.dll . - -osx : - g++ -I $(SYSFWPATH)/SDL2.framework/Headers $(INC) \ - -I $(SYSFWPATH)/SDL2_image.framework/Headers -Wl,-rpath,$(SYSFWPATH) \ - -framework SDL2 -framework SDL2_image -framework OpenGL main.cpp sdl2-gfx/SDL2_rotozoom.c \ - -o main - -osx-bundle : - if [ ! -d "$(APPDIR)" ]; then mkdir -p $(APPDIR); fi; - if [ ! -d "$(APPDIR)" ]; then mkdir $(APPDIR); fi; - if [ ! -d "$(APPDIR)/MacOS" ]; then mkdir $(APPDIR)/MacOS; fi; - if [ ! -d "$(APPDIR)/Frameworks" ]; then mkdir $(APPDIR)/Frameworks; fi; - if [ ! -d "$(APPDIR)/Resources" ]; then mkdir $(APPDIR)/Resources; fi; - touch $(APPDIR)/Info.plist - cp -r $(SYSFWPATH)/SDL2.framework $(APPDIR)/Frameworks - cp -r $(SYSFWPATH)/SDL2_image.framework $(APPDIR)/Frameworks - g++ -I $(SYSFWPATH)/SDL2.framework/Headers -I $(SYSFWPATH)/SDL2_image.framework/Headers \ - -Wl,-rpath,@executable_path/../Frameworks -Wl,-rpath,$(SYSFWPATH) \ - -framework SDL2 -framework SDL2_image -framework OpenGL main.cpp -o $(APPDIR)/MacOS/main - -cross : linux android emscripten mingw - -# main : main.o -# g++ -o main $(LFLAGS) -lGL main.o - -# main.o : main.cpp -# g++ -c $(CFLAGS) -DLINUX main.cpp diff --git a/demo/cube/BPmono.ttf b/demo/cube/BPmono.ttf new file mode 120000 index 0000000..80db3f8 --- /dev/null +++ b/demo/cube/BPmono.ttf @@ -0,0 +1 @@ +../../BPmono.ttf \ No newline at end of file diff --git a/demo/Demo.cpp b/demo/cube/Cube.cpp similarity index 81% rename from demo/Demo.cpp rename to demo/cube/Cube.cpp index 72ff9fd..6075e33 100644 --- a/demo/Demo.cpp +++ b/demo/cube/Cube.cpp @@ -27,7 +27,7 @@ ***/ -#include "Demo.hpp" +#include "Cube.hpp" char* file_to_buf(const char *path) { @@ -104,36 +104,6 @@ GLuint get_gl_texture_from_surface(SDL_Surface *surface, GLint mipmap_filter, bo return id; } -SDL_Surface* get_framerate_indicator_surface(int frame_count) -{ - TTF_Font* font = TTF_OpenFont("resource/SourceCodePro-Regular.otf", 14); - std::string padded = sfw::pad(frame_count, 2); - SDL_Surface* shaded = TTF_RenderText_Shaded( - font, padded.c_str(), {0, 0, 0}, {255, 255, 255}); - SDL_Surface* converted = SDL_ConvertSurfaceFormat( - shaded, SDL_PIXELFORMAT_ARGB8888, 0); - SDL_Surface* flipped = zoomSurface(converted, 1, -1, SMOOTHING_OFF); - SDL_FreeSurface(shaded); - SDL_FreeSurface(converted); - if (not flipped) - { - fprintf(stderr, "Could not create text %s\n", SDL_GetError()); - } - TTF_CloseFont(font); - return flipped; -} - -void set_framerate_indicator(int frame_count, GLuint id) -{ - SDL_Surface* message = get_framerate_indicator_surface(frame_count); - glBindTexture(GL_TEXTURE_2D, id); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, message->w, message->h, GL_BGRA, - GL_UNSIGNED_BYTE, message->pixels); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - SDL_FreeSurface(message); -} - Mushroom::Mushroom(Node *parent) : Sprite(parent, "resource/shrooms") { set_frame_length(500); @@ -142,41 +112,41 @@ Mushroom::Mushroom(Node *parent) : Sprite(parent, "resource/shrooms") void Mushroom::update() { - move(direction); - int x = box.left(); + move_weighted({direction, 0}); + int x = get_left(); glm::ivec2 resolution = get_display().window_size(); if (x > resolution.x or x < 0) { direction = -direction; if (x > resolution.x) { - move(resolution.x - x); + move({resolution.x - x, 0}); } else { - move(-x); + move({-x, 0}); } } Sprite::update(); } -Demo::Demo() +Cube::Cube() { Mix_Music *music = Mix_LoadMUS("resource/Field.mp3"); // Mix_Music *music = Mix_LoadMUS("/home/frank/WATERMELON-clean.mp3"); Mix_PlayMusic(music, -1); load_gl_context(); - delegate.subscribe(&Demo::respond, this); + delegate.subscribe(&Cube::respond, this); } -void Demo::load_sdl_context() +void Cube::load_sdl_context() { Game::load_sdl_context(); grass.load(); mushroom.load(); } -void Demo::load_gl_context() +void Cube::load_gl_context() { Game::load_gl_context(); grass.unload(); @@ -221,19 +191,12 @@ void Demo::load_gl_context() }; GLuint background_colors_buffer; glGenBuffers(1, &background_colors_buffer); - std::array framerate_indicator_vertices = { - { - {.9, 1, 0}, {1, 1, 0}, {.9, .9, 0}, - {1, 1, 0}, {1, .9, 0}, {.9, .9, 0} - }}; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); std::vector vertices; - vertices.reserve(cube.size() + background_vertices.size() + framerate_indicator_vertices.size()); + vertices.reserve(cube.size() + background_vertices.size()); vertices.insert(vertices.begin(), cube.begin(), cube.end()); vertices.insert(vertices.end(), background_vertices.begin(), background_vertices.end()); - vertices.insert(vertices.end(), framerate_indicator_vertices.begin(), - framerate_indicator_vertices.end()); glm::ivec2 resolution = get_display().window_size(); projection = glm::perspective( glm::radians(45.0f), resolution.x / (float) resolution.y, 0.1f, @@ -253,11 +216,6 @@ void Demo::load_gl_context() // face_ids.push_back(get_gl_texture_from_surface(surface, GL_LINEAR)); // SDL_FreeSurface(surface); // } - std::array framerate_indicator_uv = { - { - {0, 1}, {1, 1}, {0, 0}, - {1, 1}, {1, 0}, {0, 0} - }}; std::array cube_uv = { { {1, 1}, {0, 1}, {0, 0}, @@ -274,10 +232,8 @@ void Demo::load_gl_context() {1, 1}, {0, 1}, {0, 0} }}; std::vector uv; - uv.reserve(cube_uv.size() + background_vertices.size() + framerate_indicator_uv.size()); + uv.reserve(cube_uv.size() + background_vertices.size()); std::copy(cube_uv.begin(), cube_uv.end(), uv.begin()); - std::copy(framerate_indicator_uv.begin(), framerate_indicator_uv.end(), - uv.begin() + cube_uv.size() + background_vertices.size()); GLuint uvbuffer; glGenBuffers(1, &uvbuffer); unsigned char fake_texture_color[4] = {255, 255, 255, 255}; @@ -330,13 +286,9 @@ void Demo::load_gl_context() // glBindTexture(GL_TEXTURE_2D, space_texture_id); // glUniform1i(sampler_uniform_id, 0); glDepthFunc(GL_LESS); - frame_count_timestamp = SDL_GetTicks(); - SDL_Surface* fps_surface = get_framerate_indicator_surface(frame_count); - framerate_texture_id = get_gl_texture_from_surface(fps_surface, GL_LINEAR); - SDL_FreeSurface(fps_surface); } -void Demo::respond(SDL_Event& event) +void Cube::respond(SDL_Event& event) { if (delegate.compare(event, "context")) { @@ -367,7 +319,7 @@ void Demo::respond(SDL_Event& event) } } -void Demo::update() +void Cube::update() { // while (SDL_PollEvent(&event)) // { @@ -440,16 +392,7 @@ void Demo::update() glDisableVertexAttribArray(1); glEnableVertexAttribArray(2); glDrawArrays(GL_TRIANGLES, 36, 6); - if (show_framerate) - { - glBindTexture(GL_TEXTURE_2D, framerate_texture_id); - glDisableVertexAttribArray(2); - glEnableVertexAttribArray(1); - glVertexAttrib3f(2, 1, 1, 1); - glDrawArrays(GL_TRIANGLES, 42, 6); - } - // printf("%s\n", glm::to_string(model).c_str()); - float rotation = .0005f * frame_length; + float rotation = .0005f * get_frame_length(); model = glm::rotate(model, rotation, glm::vec3(0.0f, 1.0f, 0.0f)); mvp = projection * view * model; glEnable(GL_DEPTH_TEST); @@ -482,40 +425,29 @@ void Demo::update() int speed = 2; if (up_active) { - grass.move(0, -speed); + grass.move_weighted({0, -speed}); } if (right_active) { - grass.move(speed); + grass.move_weighted({speed, 0}); } if (down_active) { - grass.move(0, speed); + grass.move_weighted({0, speed}); } if (left_active) { - grass.move(-speed, 0); + grass.move_weighted({-speed, 0}); } grass.update(); mushroom.update(); - SDL_RenderPresent(renderer); - } - frame_count++; - if (ticks - frame_count_timestamp >= 1000) - { - frame_count_timestamp = ticks; - if (is_gl_context and show_framerate) - { - set_framerate_indicator(frame_count, framerate_texture_id); - } - frame_count = 0; } } -int main(int argc, char *argv[]) +int main() { - Demo demo = Demo(); - demo.run(); - demo.quit(); + Cube cube = Cube(); + cube.run(); + cube.quit(); return 0; } diff --git a/demo/Demo.hpp b/demo/cube/Cube.hpp similarity index 82% rename from demo/Demo.hpp rename to demo/cube/Cube.hpp index d464855..292fe5f 100644 --- a/demo/Demo.hpp +++ b/demo/cube/Cube.hpp @@ -14,17 +14,6 @@ #include "sdl2-gfx/SDL2_gfxPrimitives.h" #include "sdl2-gfx/SDL2_rotozoom.h" -// #if defined(__LINUX__) or defined(__MINGW32__) -// #define GL_GLEXT_PROTOTYPES -// #include -// #elif defined(__ANDROID__) -// #include -// #elif defined(__EMSCRIPTEN__) -// #include -// #elif defined(__APPLE__) -// #include -// #endif - #define GLM_ENABLE_EXPERIMENTAL #include "glm/gtx/string_cast.hpp" #include "glm/gtx/transform.hpp" @@ -50,7 +39,7 @@ struct Mushroom : Sprite }; -struct Demo : Game +struct Cube : Game { SDL_Texture *grass_texture; @@ -66,7 +55,7 @@ struct Demo : Game std::vector face_ids; float amount_rotated; - Demo(); + Cube(); void load_sdl_context(); void load_gl_context(); void respond(SDL_Event&); diff --git a/demo/cube/Makefile b/demo/cube/Makefile new file mode 100644 index 0000000..34c289c --- /dev/null +++ b/demo/cube/Makefile @@ -0,0 +1,175 @@ +# /\ +--------------------------------------------------------------+ +# ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | +# \ / / | copy, modify and sell without restriction | +# +--\ ^__^ /--+ | | +# | ~/ \~ | | - originally created at [http://nugget.fun] | +# | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ +# | SPACE ~~~~~ | / +# | ~~~~~~~ BOX |/ +# +--------------+ +# +# [ Makefile for cube demo ] +# +# This should build the cube demo for Linux. Other platforms haven't been tested in +# a while and won't work without significant editing. +# +# Edit the parameters as necessary and run `make linux` in the current directory. +# + +####################### +# Location parameters # +####################### + +# Location of source files for the demo +SRC_DIR := ./ + +# Locations of [SPACE BOX] source and dependencies required to be compiled from source. These +# locations are configured to match the structure of the [SPACE BOX] repository but can be +# modified as necessary. +SB_DIR := ../../ +SB_SRC_DIR := $(SB_DIR)src/ +SB_LIB_DIR := $(SB_DIR)lib/ +SDLGFX2_DIR := $(SB_LIB_DIR)sdl2-gfx/ +GLEW_DIR := $(SB_LIB_DIR)glew/ + +# C and C++ compiler commands +CC_LINUX := clang +CPPC_LINUX := clang++ + +# Location of SDL config program +SDLCONFIG := ~/local/sdl/bin/sdl2-config + +# Edit to point to the location of BPmono.ttf +CREATE_FONT_SYMLINK := ln -nsf $(SB_DIR)"BPmono.ttf" . + +############################# +# Based on above parameters # +############################# + +CFLAGS := -Wall -O0 -c -I$(SB_LIB_DIR) -I$(SB_SRC_DIR) -g $(shell $(SDLCONFIG) --cflags) +CPP_FLAGS := $(CFLAGS) --std=c++17 +LFLAGS := $(shell $(SDLCONFIG) --libs) -lpthread +SFW_H_FILES := $(wildcard $(addprefix $(SB_SRC_DIR),*.hpp)) +SFW_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o),$(SFW_H_FILES:.hpp=.o)) +SRC_H_FILES := $(wildcard $(addprefix $(SRC_DIR),*.hpp)) +SRC_O_FILES := $(SRC_H_FILES:.hpp=.o) + +################################################################## +# Targets for building [SPACE BOX], dependencies and demo source # +################################################################## + +$(SDLGFX2_DIR)%.o: $(SDLGFX2_DIR)%.c $(SDLGFX2_DIR)%.h +$(GLEW_DIR)%.o: $(GLEW_DIR)%.c $(GLEW_DIR)%.h + $(CC_LINUX) $(CFLAGS) $< -c -o $@ + +$(SB_SRC_DIR)extension.o : $(addprefix $(SB_SRC_DIR),Box.hpp Segment.hpp Color.hpp filesystem.hpp Pixels.hpp) +$(SB_SRC_DIR)Node.o : $(addprefix $(SB_SRC_DIR),Game.hpp Configuration.hpp Delegate.hpp Display.hpp Input.hpp Box.hpp Audio.hpp) +$(SB_SRC_DIR)Sprite.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Animation.hpp Color.hpp extension.hpp Pixels.hpp) +$(SB_SRC_DIR)Game.o : $(addprefix $(SB_SRC_DIR),extension.hpp Node.hpp Sprite.hpp Recorder.hpp Input.hpp Configuration.hpp \ + Delegate.hpp Audio.hpp) +$(SB_SRC_DIR)Animation.o : $(addprefix $(SB_SRC_DIR),Node.hpp Timer.hpp) +$(SB_SRC_DIR)Recorder.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Configuration.hpp Delegate.hpp Animation.hpp extension.hpp) +$(SB_SRC_DIR)Input.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp Configuration.hpp Delegate.hpp) +$(SB_SRC_DIR)Configuration.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp) +$(SB_SRC_DIR)Delegate.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Input.hpp) +$(SB_SRC_DIR)Display.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Configuration.hpp Delegate.hpp) +$(SB_SRC_DIR)Box.o : $(addprefix $(SB_SRC_DIR),extension.hpp Segment.hpp) +$(SB_SRC_DIR)Segment.o : $(addprefix $(SB_SRC_DIR),extension.hpp Box.hpp) +$(SB_SRC_DIR)Pixels.o : $(addprefix $(SB_SRC_DIR),Box.hpp extension.hpp) +$(SB_SRC_DIR)Audio.o : $(addprefix $(SB_SRC_DIR),Node.hpp Display.hpp Configuration.hpp Box.hpp filesystem.hpp extension.hpp) +$(SRC_DIR)Cube.o : $(SFW_H_FILES) +%.o : %.cpp %.hpp + $(CPPC_LINUX) $(CPP_FLAGS) $< -c -o $@ + +########################## +# Target for Linux build # +########################## + +linux : $(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) \ + $(SRC_O_FILES) $(SFW_O_FILES) + $(CREATE_FONT_SYMLINK) + $(CPPC_LINUX) $(LFLAGS) -D__LINUX__ $^ -lGL -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs -o cube + +####################################### +# Target for cleaning up object files # +####################################### + +clean : + - rm *.o + - rm $(SB_SRC_DIR)*.o + - rm $(GLEW_DIR)*.o + - rm $(SDLGFX2_DIR)*.o + +# +# These assignments are necessary for cross-compiling to Android, web (via emscripten), Windows (via mingw), +# and OS/X. Cross-compilation targets have not been tested in a while and won't compile without significant changes. +# + +export ANDROID_HOME = /home/frank/ext/software/android-sdk +export ANDROID_NDK_HOME = /home/frank/ext/software/android-ndk-r8d +BUILDDIR := build +ANDROIDPROJECT := com.nuggetsselect.demo +SOFTWARE_ROOT := /home/frank/ext/software +SDLHOME := $(SOFTWARE_ROOT)/SDL2-2.0.14 +SDL_IMG_HOME := $(SOFTWARE_ROOT)/SDL2_image-2.0.5 +SDL_TTF_HOME := $(SOFTWARE_ROOT)/SDL2_ttf-2.0.15 +GLEW_WIN32_HOME := $(SOFTWARE_ROOT)/glew-2.1.0-win32 +PROJECTHOME := $(shell pwd) +EMSCRIPTENHOME := /home/frank/ext/software/emsdk/emscripten/tag-1.38.12 +SDLEMLIBSHOME := $(SDLHOME)/build/em/build/.libs +EMBUILDDIR := em +WINBUILDDIR := win +SDLMINGWHOME := $(SDLHOME)/i686-w64-mingw32 +SDL_IMG_MINGW_HOME := $(SDL_IMG_HOME)/i686-w64-mingw32 +SDL_TTF_MINGW_HOME := $(SDL_TTF_HOME)/i686-w64-mingw32 +APPDIR := Main.app/Contents +SYSFWPATH := /Library/Frameworks + +android : + if [ ! -d $(BUILDDIR) ]; then mkdir $(BUILDDIR); fi; + cd $(SDLHOME)/build-scripts/ && \ + ./androidbuild.sh $(ANDROIDPROJECT) $(PROJECTHOME)/main.cpp + cp -r $(SDLHOME)/build/$(ANDROIDPROJECT) $(BUILDDIR) + sed -i s/2\.3\.3/2\.2\.3/g $(BUILDDIR)/$(ANDROIDPROJECT)/build.gradle + sed -i s/26\.0\.1/23\.0\.1/g $(BUILDDIR)/$(ANDROIDPROJECT)/app/build.gradle + cd $(BUILDDIR)/$(ANDROIDPROJECT) && ./gradlew assembleDebug + +emscripten : + if [ ! -d $(BUILDDIR)/$(EMBUILDDIR) ]; then mkdir -p $(BUILDDIR)/$(EMBUILDDIR); fi; + cd $(BUILDDIR)/$(EMBUILDDIR) && \ + $(EMSCRIPTENHOME)/em++ -O2 $(PROJECTHOME)/main.cpp -I$(SDLHOME)/include \ + -Wall -s USE_SDL=2 -o main.html + +mingw : + if [ ! -d $(BUILDDIR)/$(WINBUILDDIR) ]; then mkdir -p $(BUILDDIR)/$(WINBUILDDIR); fi; + cd $(BUILDDIR)/$(WINBUILDDIR) && \ + i686-w64-mingw32-g++ -std=c++17 $(PROJECTHOME)/main.cpp -I$(SDLMINGWHOME)/include/SDL2 \ + -I$(SDL_IMG_MINGW_HOME)/include/SDL2 -I$(SDL_TTF_MINGW_HOME)/include/SDL2 $(INC) \ + $(PROJECTHOME)/sdl2-gfx/SDL2_gfxPrimitives.c $(PROJECTHOME)/sdl2-gfx/SDL2_rotozoom.c $(PROJECTHOME)/glew/glew.c \ + -L$(SDLMINGWHOME)/lib -L$(SDL_IMG_MINGW_HOME)/lib -L$(SDL_TTF_MINGW_HOME)/lib \ + -lmingw32 -lSDL2_image \ + -lSDL2_ttf -lstdc++fs \ + -lSDL2main -lSDL2 -lopengl32 -Wall -O2 -o main.exe && \ + cp $(SDLMINGWHOME)/bin/SDL2.dll $(SDL_IMG_MINGW_HOME)/bin/SDL2_image.dll \ + $(SDL_TTF_MINGW_HOME)/bin/SDL2_ttf.dll . + +osx : + g++ -I $(SYSFWPATH)/SDL2.framework/Headers $(INC) \ + -I $(SYSFWPATH)/SDL2_image.framework/Headers -Wl,-rpath,$(SYSFWPATH) \ + -framework SDL2 -framework SDL2_image -framework OpenGL main.cpp sdl2-gfx/SDL2_rotozoom.c \ + -o main + +osx-bundle : + if [ ! -d "$(APPDIR)" ]; then mkdir -p $(APPDIR); fi; + if [ ! -d "$(APPDIR)" ]; then mkdir $(APPDIR); fi; + if [ ! -d "$(APPDIR)/MacOS" ]; then mkdir $(APPDIR)/MacOS; fi; + if [ ! -d "$(APPDIR)/Frameworks" ]; then mkdir $(APPDIR)/Frameworks; fi; + if [ ! -d "$(APPDIR)/Resources" ]; then mkdir $(APPDIR)/Resources; fi; + touch $(APPDIR)/Info.plist + cp -r $(SYSFWPATH)/SDL2.framework $(APPDIR)/Frameworks + cp -r $(SYSFWPATH)/SDL2_image.framework $(APPDIR)/Frameworks + g++ -I $(SYSFWPATH)/SDL2.framework/Headers -I $(SYSFWPATH)/SDL2_image.framework/Headers \ + -Wl,-rpath,@executable_path/../Frameworks -Wl,-rpath,$(SYSFWPATH) \ + -framework SDL2 -framework SDL2_image -framework OpenGL main.cpp -o $(APPDIR)/MacOS/main + +cross : linux android emscripten mingw diff --git a/demo/config.json b/demo/cube/config.json similarity index 78% rename from demo/config.json rename to demo/cube/config.json index ec5d21a..bb174db 100644 --- a/demo/config.json +++ b/demo/cube/config.json @@ -10,10 +10,14 @@ "print-video-memory-size": "v", "play-sound": "" }, + "input": + { + "suppress-any-key-on-mods": true + }, "recording": { "screenshot-directory": "local/screenshots", - "video-directory": "local/video" + "video-directory": "local/video", "enabled": true, "write-mp4": true, "video-frame-length": 16.667, diff --git a/demo/resource/Ag.ogg b/demo/cube/resource/Ag.ogg similarity index 100% rename from demo/resource/Ag.ogg rename to demo/cube/resource/Ag.ogg diff --git a/demo/resource/Field.mp3 b/demo/cube/resource/Field.mp3 similarity index 100% rename from demo/resource/Field.mp3 rename to demo/cube/resource/Field.mp3 diff --git a/demo/resource/Field.png b/demo/cube/resource/Field.png similarity index 100% rename from demo/resource/Field.png rename to demo/cube/resource/Field.png diff --git a/demo/resource/background.png b/demo/cube/resource/background.png similarity index 100% rename from demo/resource/background.png rename to demo/cube/resource/background.png diff --git a/demo/resource/shrooms/anxiety.png b/demo/cube/resource/shrooms/anxiety.png similarity index 100% rename from demo/resource/shrooms/anxiety.png rename to demo/cube/resource/shrooms/anxiety.png diff --git a/demo/resource/shrooms/lapis-lazuli.png b/demo/cube/resource/shrooms/lapis-lazuli.png similarity index 100% rename from demo/resource/shrooms/lapis-lazuli.png rename to demo/cube/resource/shrooms/lapis-lazuli.png diff --git a/demo/resource/shrooms/mr-plummy.png b/demo/cube/resource/shrooms/mr-plummy.png similarity index 100% rename from demo/resource/shrooms/mr-plummy.png rename to demo/cube/resource/shrooms/mr-plummy.png diff --git a/demo/resource/shrooms/poison-shibake.png b/demo/cube/resource/shrooms/poison-shibake.png similarity index 100% rename from demo/resource/shrooms/poison-shibake.png rename to demo/cube/resource/shrooms/poison-shibake.png diff --git a/demo/resource/shrooms/shaver-buzz.png b/demo/cube/resource/shrooms/shaver-buzz.png similarity index 100% rename from demo/resource/shrooms/shaver-buzz.png rename to demo/cube/resource/shrooms/shaver-buzz.png diff --git a/demo/resource/shrooms/sombrero.png b/demo/cube/resource/shrooms/sombrero.png similarity index 100% rename from demo/resource/shrooms/sombrero.png rename to demo/cube/resource/shrooms/sombrero.png diff --git a/demo/resource/shrooms/spider-house.png b/demo/cube/resource/shrooms/spider-house.png similarity index 100% rename from demo/resource/shrooms/spider-house.png rename to demo/cube/resource/shrooms/spider-house.png diff --git a/demo/resource/shrooms/teethmouth.png b/demo/cube/resource/shrooms/teethmouth.png similarity index 100% rename from demo/resource/shrooms/teethmouth.png rename to demo/cube/resource/shrooms/teethmouth.png diff --git a/demo/resource/shrooms/terraformer.png b/demo/cube/resource/shrooms/terraformer.png similarity index 100% rename from demo/resource/shrooms/terraformer.png rename to demo/cube/resource/shrooms/terraformer.png diff --git a/demo/resource/tile.png b/demo/cube/resource/tile.png similarity index 100% rename from demo/resource/tile.png rename to demo/cube/resource/tile.png diff --git a/demo/shaders/flat.frag b/demo/cube/shaders/flat.frag similarity index 100% rename from demo/shaders/flat.frag rename to demo/cube/shaders/flat.frag diff --git a/demo/shaders/flat.vert b/demo/cube/shaders/flat.vert similarity index 100% rename from demo/shaders/flat.vert rename to demo/cube/shaders/flat.vert diff --git a/demo/shaders/triangle.frag b/demo/cube/shaders/triangle.frag similarity index 100% rename from demo/shaders/triangle.frag rename to demo/cube/shaders/triangle.frag diff --git a/demo/shaders/triangle.vert b/demo/cube/shaders/triangle.vert similarity index 100% rename from demo/shaders/triangle.vert rename to demo/cube/shaders/triangle.vert diff --git a/demo/resource/SourceCodePro-Regular.otf b/demo/resource/SourceCodePro-Regular.otf deleted file mode 100755 index bcaa0a9..0000000 Binary files a/demo/resource/SourceCodePro-Regular.otf and /dev/null differ diff --git a/demo/squircle/BPmono.ttf b/demo/squircle/BPmono.ttf new file mode 120000 index 0000000..80db3f8 --- /dev/null +++ b/demo/squircle/BPmono.ttf @@ -0,0 +1 @@ +../../BPmono.ttf \ No newline at end of file diff --git a/demo/squircle/Makefile b/demo/squircle/Makefile new file mode 100644 index 0000000..4fb0d36 --- /dev/null +++ b/demo/squircle/Makefile @@ -0,0 +1,101 @@ +# /\ +--------------------------------------------------------------+ +# ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | +# \ / / | copy, modify and sell without restriction | +# +--\ ^__^ /--+ | | +# | ~/ \~ | | - originally created at [http://nugget.fun] | +# | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ +# | SPACE ~~~~~ | / +# | ~~~~~~~ BOX |/ +# +--------------+ +# +# [ Makefile for squircle demo ] +# +# This should build the squircle for Linux. Compilation to other platforms hasn't been +# attempted yet. +# +# Edit the parameters as necessary and run `make linux` in the current directory. +# + +####################### +# Location parameters # +####################### + +# Location of source files for the demo +SRC_DIR := ./ + +# Locations of [SPACE BOX] source and dependencies required to be compiled from source. These +# locations are configured to match the structure of the [SPACE BOX] repository but can be +# modified as necessary. +SB_DIR := ../../ +SB_SRC_DIR := $(SB_DIR)src/ +SB_LIB_DIR := $(SB_DIR)lib/ +SDLGFX2_DIR := $(SB_LIB_DIR)sdl2-gfx/ +GLEW_DIR := $(SB_LIB_DIR)glew/ + +# C and C++ compiler commands +CC := clang +CPPC := clang++ + +# Location of SDL config program +SDLCONFIG := ~/local/sdl/bin/sdl2-config + +# Edit to point to the location of BPmono.ttf +CREATE_FONT_SYMLINK := ln -nsf $(SB_DIR)"BPmono.ttf" . + +############################# +# Based on above parameters # +############################# + +CFLAGS := -Wall -O0 -c -I$(SB_LIB_DIR) -I$(SB_SRC_DIR) $(shell $(SDLCONFIG) --cflags) -g +CPP_FLAGS := $(CFLAGS) --std=c++17 +LFLAGS := $(shell $(SDLCONFIG) --libs) -lpthread +SB_H_FILES := $(wildcard $(addprefix $(SB_SRC_DIR),*.hpp)) +SB_O_FILES := $(filter-out $(addprefix $(SB_SRC_DIR),filesystem.o),$(SB_H_FILES:.hpp=.o)) +SRC_H_FILES := $(wildcard $(addprefix $(SRC_DIR),*.hpp)) +SRC_O_FILES := $(SRC_H_FILES:.hpp=.o) + +################################################################## +# Targets for building [SPACE BOX], dependencies and demo source # +################################################################## + +$(SDLGFX2_DIR)%.o: $(SDLGFX2_DIR)%.c $(SDLGFX2_DIR)%.h +$(GLEW_DIR)%.o: $(GLEW_DIR)%.c $(GLEW_DIR)%.h + $(CC) $(CFLAGS) $< -c -o $@ + +$(SB_SRC_DIR)extension.o : $(addprefix $(SB_SRC_DIR),Box.hpp Segment.hpp Color.hpp filesystem.hpp Pixels.hpp) +$(SB_SRC_DIR)Node.o : $(addprefix $(SB_SRC_DIR),Game.hpp Configuration.hpp Delegate.hpp Display.hpp Input.hpp Box.hpp Audio.hpp) +$(SB_SRC_DIR)Sprite.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Animation.hpp Color.hpp extension.hpp Pixels.hpp) +$(SB_SRC_DIR)Game.o : $(addprefix $(SB_SRC_DIR),extension.hpp Node.hpp Sprite.hpp Recorder.hpp Input.hpp Configuration.hpp \ + Delegate.hpp Audio.hpp) +$(SB_SRC_DIR)Animation.o : $(addprefix $(SB_SRC_DIR),Node.hpp Timer.hpp) +$(SB_SRC_DIR)Recorder.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Configuration.hpp Delegate.hpp Animation.hpp extension.hpp) +$(SB_SRC_DIR)Input.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp Configuration.hpp Delegate.hpp) +$(SB_SRC_DIR)Configuration.o : $(addprefix $(SB_SRC_DIR),Node.hpp Animation.hpp) +$(SB_SRC_DIR)Delegate.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Input.hpp) +$(SB_SRC_DIR)Display.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Configuration.hpp Delegate.hpp) +$(SB_SRC_DIR)Box.o : $(addprefix $(SB_SRC_DIR),extension.hpp Segment.hpp) +$(SB_SRC_DIR)Segment.o : $(addprefix $(SB_SRC_DIR),extension.hpp Box.hpp) +$(SB_SRC_DIR)Pixels.o : $(addprefix $(SB_SRC_DIR),Box.hpp extension.hpp) +$(SB_SRC_DIR)Audio.o : $(addprefix $(SB_SRC_DIR),Node.hpp Display.hpp Configuration.hpp Box.hpp filesystem.hpp extension.hpp) +$(SRC_DIR)Squircle.o : $(SB_H_FILES) +%.o : %.cpp %.hpp + $(CPPC) $(CPP_FLAGS) $< -c -o $@ + +########################## +# Target for Linux build # +########################## + +linux : $(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) \ + $(SB_O_FILES) $(SRC_O_FILES) + $(CREATE_FONT_SYMLINK) + $(CPPC) $(LFLAGS) -D__LINUX__ -lGL -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs $^ -o squircle + +####################################### +# Target for cleaning up object files # +####################################### + +clean : + - rm *.o + - rm $(SB_SRC_DIR)*.o + - rm $(GLEW_DIR)*.o + - rm $(SDLGFX2_DIR)*.o diff --git a/demo/squircle/Squircle.cpp b/demo/squircle/Squircle.cpp new file mode 100644 index 0000000..5543099 --- /dev/null +++ b/demo/squircle/Squircle.cpp @@ -0,0 +1,182 @@ +/* + /\ +--------------------------------------------------------------+ + ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | + \ / / | copy, modify and sell without restriction | + +--\ ^__^ /--+ | | + | ~/ \~ | | - originally created at [http:nugget.fun] | + | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ + | SPACE ~~~~~ | / + | ~~~~~~~ BOX |/ + +--------------+ + + Demonstrates functions from the Box class that map vertices from a square to a circle + and from a circle to a square. The functions are based on the equations described at + http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html + +*/ + +#include "Squircle.hpp" + +int main() +{ + Squircle squircle = Squircle(); + squircle.run(); + squircle.quit(); + return 0; +} + +Squircle::Squircle() +{ + /* subscribe to command events */ + get_delegate().subscribe(&Squircle::respond, this); + /* testing graphics in GL context */ + load_gl_context(); +} + +void Squircle::load_gl_context() +{ + super::load_gl_context(); + /* 2D vertices for any texture that is a plane spanning the screen */ + std::array plane_vertices = {{ + {-1.0f, 1.0f}, {1.0f, 1.0f}, {-1.0f, -1.0f}, + {1.0f, 1.0f}, {1.0f, -1.0f}, {-1.0f, -1.0f} + }}; + std::vector circle_vertices = sfw::points_on_circle(get_configuration()["circle-resolution"]); + circle_vertices.insert(circle_vertices.begin(), {0.0f, 0.0f}); + circle_vertices.insert(circle_vertices.end(), circle_vertices[1]); + circle_vertices_count = circle_vertices.size(); + /* Generate vertex buffer object to hold both mapped and unmapped data */ + GLuint vbo; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + /* Allocate space for plane and circle and copy plane vertices in VBO */ + GLsizeiptr vbo_size = (plane_vertices.size() + circle_vertices.size()) * sizeof(glm::vec2); + glBufferData(GL_ARRAY_BUFFER, vbo_size, plane_vertices.data(), GL_STATIC_DRAW); + /* Allocate VAO for the plane vertices and connect attributes to the VBO */ + glGenVertexArrays(1, &unmapped_vao); + glBindVertexArray(unmapped_vao); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + glEnableVertexAttribArray(0); + /* Copy circle vertices into VBO */ + GLintptr offset = plane_vertices.size() * sizeof(glm::vec2); + glBufferSubData(GL_ARRAY_BUFFER, offset, circle_vertices.size() * sizeof(glm::vec2), circle_vertices.data()); + /* Allocate VAO for the circle vertices and connect attributes to the VBO */ + glGenVertexArrays(1, &mapped_vao); + glBindVertexArray(mapped_vao); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast(offset)); + glEnableVertexAttribArray(0); + /* Load flat shader program */ + GLuint vertex_shader = load_shader("flat.vert", GL_VERTEX_SHADER); + GLuint fragment_shader = load_shader("flat.frag", GL_FRAGMENT_SHADER); + flat_program = glCreateProgram(); + glBindAttribLocation(flat_program, 0, "position"); + glAttachShader(flat_program, vertex_shader); + glAttachShader(flat_program, fragment_shader); + link_shader(flat_program); + glUseProgram(flat_program); + /* load image */ + load_image_index(0); + base_texture_shader_location = glGetUniformLocation(flat_program, "base_texture"); + mode_uniform_location = glGetUniformLocation(flat_program, "mode"); + transformation_uniform_location = glGetUniformLocation(flat_program, "transformation"); + log_gl_errors(); +} + +/* Load image at path as an SDL surface, generate texture to load pixel data into, allocate storage, and bind + * and edit texture properties. Returns the ID of the generated texture. */ +GLuint Squircle::load_file_into_texture(fs::path path) const +{ + GLuint texture_id; + std::unique_ptr surface(IMG_Load(path.c_str()), SDL_FreeSurface); + std::unique_ptr flipped_surface(rotozoomSurfaceXY(surface.get(), 0, 1, -1, 0), SDL_FreeSurface); + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, flipped_surface->w, flipped_surface->h); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, flipped_surface->w, flipped_surface->h, GL_RGBA, GL_UNSIGNED_BYTE, flipped_surface->pixels); + std::ostringstream message; + message << "loaded image into texture id #" << texture_id << " " << flipped_surface->w << " x " << flipped_surface->h; + log(message.str()); + return texture_id; +} + +void Squircle::load_image_index(int index) +{ + std::vector paths = sfw::glob("images/.*"); + fs::path path = paths[index % paths.size()]; + std::ostringstream message; + message << "loading " << path; + log(message.str()); + image_index = index; + base_texture_id = load_file_into_texture(path); +} + +void Squircle::respond(SDL_Event& event) +{ + if (get_delegate().compare(event, "next")) + { + load_image_index(image_index + 1); + } + else if (get_delegate().compare(event, "mode")) + { + mode = mode == Mode::SQUIRCLE ? Mode::UNSQUIRCLE : Mode::SQUIRCLE; + } + else if (get_delegate().compare(event, "left")) + { + spin_z = Spin::NEGATIVE; + } + else if (get_delegate().compare(event, "right")) + { + spin_z = Spin::POSITIVE; + } + else if (get_delegate().compare(event, {std::string("left"), std::string("right")}, false, true)) + { + spin_z = Spin::NONE; + } + else if (get_delegate().compare(event, "up")) + { + spin_x = Spin::NEGATIVE; + } + else if (get_delegate().compare(event, "down")) + { + spin_x = Spin::POSITIVE; + } + else if (get_delegate().compare(event, {std::string("up"), std::string("down")}, false, true)) + { + spin_x = Spin::NONE; + } +} + +void Squircle::update() +{ + /* apply rotation to transformation matrix */ + float rotation_speed = get_configuration()["rotation-speed"]; + transformation = glm::rotate(transformation, spin_z * rotation_speed, {0, 0, 1}); + transformation = glm::rotate(transformation, spin_x * rotation_speed, {1, 0, 0}); + glUniformMatrix4fv(transformation_uniform_location, 1, false, &transformation[0][0]); + /* viewport box will be used to tell GL where to draw */ + Box viewport_box = window_box(); + glDisable(GL_DEPTH_TEST); + /* paint the screen black */ + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + /* set uniform and activate texture */ + glUniform1i(base_texture_shader_location, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, base_texture_id); + /* set viewport to left side of screen for unmapped plane */ + glViewport(viewport_box.left(), viewport_box.top(), viewport_box.width() / 2, viewport_box.height()); + /* draws plane vertices and plane UV */ + glBindVertexArray(unmapped_vao); + glUniform1i(mode_uniform_location, mode == Mode::UNSQUIRCLE ? 2 : 0); + glDrawArrays(GL_TRIANGLES, 0, 6); + /* set viewport to right side of screen for mapped plane */ + glUseProgram(flat_program); + glViewport(viewport_box.cx(), viewport_box.top(), viewport_box.width() / 2, viewport_box.height()); + /* draws mapped plane vertices and plane UV */ + glBindVertexArray(mapped_vao); + glUniform1i(mode_uniform_location, mode == Mode::SQUIRCLE ? 1 : 0); + glDrawArrays(GL_TRIANGLE_FAN, 0, circle_vertices_count); + SDL_GL_SwapWindow(get_window()); +} diff --git a/demo/squircle/Squircle.hpp b/demo/squircle/Squircle.hpp new file mode 100644 index 0000000..678ab0a --- /dev/null +++ b/demo/squircle/Squircle.hpp @@ -0,0 +1,47 @@ +#ifndef Squircle_h_ +#define Squircle_h_ + +#include "glm/glm.hpp" +#include "SDL.h" +#include "Game.hpp" +#include "filesystem.hpp" + +class Squircle : public Game +{ + +private: + + enum Mode : bool + { + SQUIRCLE, + UNSQUIRCLE + }; + + enum Spin + { + NEGATIVE = -1, + NONE, + POSITIVE = 1 + }; + + GLuint base_texture_shader_location = 0, base_texture_id = 0, flat_program = 0, unmapped_vao = 0, mapped_vao = 0, + mode_uniform_location = 0, transformation_uniform_location = 0; + int circle_vertices_count = 0, image_index = 0; + Mode mode = Mode::SQUIRCLE; + glm::mat4 transformation = glm::mat4(1.0f); + Spin spin_z = Spin::NONE, spin_x = Spin::NONE; + + typedef Game super; + void load_gl_context(); + GLuint load_file_into_texture(fs::path) const; + void load_image_index(int); + void update(); + +public: + + Squircle(); + void respond(SDL_Event&); + +}; + +#endif diff --git a/demo/squircle/config.json b/demo/squircle/config.json new file mode 100644 index 0000000..65e5b99 --- /dev/null +++ b/demo/squircle/config.json @@ -0,0 +1,27 @@ +{ + "circle-resolution": 48, + "rotation-speed": 0.1, + "recording": + { + "screenshot-directory": "local/screenshots", + "video-directory": "local/video", + "enabled": true, + "write-mp4": true, + "video-frame-length": 33.333, + "max-video-memory": 2000, + "mp4-pixel-format": "yuv420p" + }, + "log": + { + "debug-to-stdout": false + }, + "configuration": + { + "auto-refresh": true + }, + "keys": + { + "next": "space", + "mode": "enter" + } +} diff --git a/demo/squircle/flat.frag b/demo/squircle/flat.frag new file mode 100644 index 0000000..2245f82 --- /dev/null +++ b/demo/squircle/flat.frag @@ -0,0 +1,51 @@ +#version 130 + +in vec2 frag_uv; +in vec2 original_coordinates; +in vec4 clip_coordinates; +uniform sampler2D base_texture; +uniform int mode; + +/* [-1, 1] normalized device coordinates to [0, 1] UV coordinates */ +vec2 ndc_to_uv(vec2 coordinates) +{ + return (coordinates + 1) / 2; +} + +/* coordinates in circle with radius <= 1 to box coordinates in [-1, 1] */ +vec2 circle_to_box(vec2 circle) +{ + float u = circle.x; + float v = circle.y; + float u_sq = pow(u, 2); + float v_sq = pow(v, 2); + float rt_2 = sqrt(2); + float x = .5 * sqrt(2 + 2 * u * rt_2 + u_sq - v_sq) - .5 * sqrt(2 - 2 * u * rt_2 + u_sq - v_sq); + float y = .5 * sqrt(2 + 2 * v * rt_2 - u_sq + v_sq) - .5 * sqrt(2 - 2 * v * rt_2 - u_sq + v_sq); + return vec2(x, y); +} + +/* box coordinates in [-1, 1] to coordinates in circle with radius <= 1 */ +vec2 box_to_circle(vec2 box) +{ + float u = box.x * sqrt(1 - .5 * pow(box.y, 2)); + float v = box.y * sqrt(1 - .5 * pow(box.x, 2)); + return vec2(u, v); +} + +void main() +{ + /* normalized device coordinates (coordinates in [-1, 1] space within viewport) */ + vec2 ndc = original_coordinates; + /* map to circle if requested */ + if (mode == 1) + { + ndc = circle_to_box(ndc); + } + else if (mode == 2) + { + ndc = box_to_circle(ndc); + } + /* translate ndc to uv space and get texel */ + gl_FragColor = texture(base_texture, ndc_to_uv(ndc)); +} diff --git a/demo/squircle/flat.vert b/demo/squircle/flat.vert new file mode 100644 index 0000000..bf1f6eb --- /dev/null +++ b/demo/squircle/flat.vert @@ -0,0 +1,21 @@ +#version 130 + +in vec2 position; +in vec2 vertex_uv; +uniform mat4 transformation; +uniform int mode; +out vec2 original_coordinates; +out vec4 clip_coordinates; +out vec2 frag_uv; + +void main(void) +{ + gl_Position = vec4(position, 0, 1); + if (mode > 0) + { + gl_Position *= transformation; + } + original_coordinates = position; + clip_coordinates = gl_Position; + frag_uv = vertex_uv; +} diff --git a/demo/squircle/images/2Ddim-L1norm-10site.png b/demo/squircle/images/2Ddim-L1norm-10site.png new file mode 100755 index 0000000..1be5048 Binary files /dev/null and b/demo/squircle/images/2Ddim-L1norm-10site.png differ diff --git a/src/Animation.cpp b/src/Animation.cpp index 45f57f9..8eb93b1 100644 --- a/src/Animation.cpp +++ b/src/Animation.cpp @@ -52,7 +52,7 @@ bool Animation::is_playing(bool include_delay) void Animation::update() { timer.update(); - if (playing and not paused and containing_object->is_active()) + if (playing && !paused && containing_object->is_active()) { if (delay > 0) { diff --git a/src/Box.cpp b/src/Box.cpp index d4c4312..d5cbb07 100644 --- a/src/Box.cpp +++ b/src/Box.cpp @@ -515,13 +515,6 @@ bool Box::collide(const Box& box, Box& overlap) const return collide(box, &overlap); } -/* Map any point in the bounds of a square with x and y between [-, radius] to a circle with specified radius. If - * center is specified, both the square and circle are centered at specified point. The default radius is 1, and the - * default center is (0, 0). Formula taken from http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html */ -glm::vec2 Box::map_point_to_circle(const glm::vec2& point, float radius, const glm::vec2& center) const -{ -} - /* Return the string representation of a Box "{left, top, width, height}" */ std::string Box::string() const { diff --git a/src/Box.hpp b/src/Box.hpp index 406571e..53afbe6 100644 --- a/src/Box.hpp +++ b/src/Box.hpp @@ -77,7 +77,6 @@ public: bool collide(const Segment&, glm::vec2&) const; bool collide(const Box&, Box* = nullptr) const; bool collide(const Box&, Box&) const; - glm::vec2 map_point_to_circle(const glm::vec2&, float, const glm::vec2&) const; virtual std::string class_name() const { return "Box"; } std::string string() const; diff --git a/src/Configuration.cpp b/src/Configuration.cpp index c6d3e49..29429d0 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -4,17 +4,20 @@ #include "Configuration.hpp" -Configuration::Configuration(Node *parent) : Configuration(parent, "config.json") {} - +/* Initialize a Configuration object. The path argument is the location where the config file is stored. + * If there is no file located at the path, it will be created if the write method is called. System level + * default assignments defined in this file can be added to and overwritten by user supplied JSON file at + * the specified path or at a path passed to the load function. */ Configuration::Configuration(Node *parent, fs::path path) : Node(parent) { config_path = path; set_defaults(); load(); - merge(); + auto_refresher.set_frame_length(config["configuration"]["auto-refresh-interval"].get()); + auto_refresh(config["configuration"]["auto-refresh"]); } -/* Fill the system level config JSON with default values set by the framework */ +/* Fill the system level config JSON dict with default values set by the framework */ void Configuration::set_defaults() { sys_config["keys"] = { @@ -34,14 +37,14 @@ void Configuration::set_defaults() sys_config["input"] = { {"suppress-any-key-on-mods", true}, {"system-any-key-ignore-commands", {"fullscreen", "screenshot", "toggle-framerate", "record", "quit"}}, - {"any-key-ignore-commands", {}}, + {"any-key-ignore-commands", nlohmann::json::array()}, {"default-unsuppress-delay", 700}, {"ignore-repeat-keypress", true} }; sys_config["display"] = { {"dimensions", {640, 480}}, {"framerate", 60}, - {"title", "sfw"}, + {"title", "[SPACE BOX]"}, {"debug", false}, {"show-cursor", false}, {"render-test-spacing", 2}, @@ -83,41 +86,90 @@ void Configuration::set_defaults() {"info-file-name", "log.txt"}, {"debug-file-name", "debug_log.txt"} }; + sys_config["configuration"] = { + {"auto-refresh", false}, + {"auto-refresh-interval", 1000} + }; + config = sys_config; } +/* Load the configuration file at path */ +void Configuration::load(fs::path path) +{ + /* read contents of path into the game level config JSON dict */ + if (fs::exists(path)) + { + std::ifstream contents(path); + contents >> user_config; + /* store modification time */ + config_file_modification_time = fs::last_write_time(path); + /* merge into the full config JSON dict */ + merge(); + } +} + +/* Load the configuration file at Configuration::config_path */ void Configuration::load() { load(config_path); } -void Configuration::load(fs::path path) -{ - if (fs::exists(path)) - { - std::ifstream contents(path); - contents >> game_config; - } -} - +/* Merge the system level config JSON dict (hard-coded in this file) with the user level config JSON + * dict (loaded from disk by the load function) */ void Configuration::merge() { - config = sys_config; - if (not game_config.empty()) + if (not user_config.empty()) { - for (auto& section: game_config.items()) + /* loop over first level key/value pairs */ + for (auto& item: user_config.items()) { - config[section.key()].update(game_config[section.key()]); + /* if the value is an object (dict), merge it into the config, overwriting keys already in the config */ + if (item.value().is_object()) + { + config[item.key()].update(item.value()); + } + /* otherwise just assign config key to this value */ + else + { + config[item.key()] = item.value(); + } } } } +/* Set auto refresh to on or off */ +void Configuration::auto_refresh(bool on) +{ + on ? auto_refresher.play() : auto_refresher.pause(); +} + +/* Refresh the config contents by calling the default load function */ +void Configuration::refresh() +{ + if (fs::exists(config_path) && fs::last_write_time(config_path) > config_file_modification_time) + { + std::ostringstream message; + message << "config file modified, reloading " << config_path; + debug(message.str()); + load(); + } +} + +/* Write configuration to specified path in JSON format */ +void Configuration::write(fs::path path) +{ + std::ofstream output(path); + output << std::setw(tab_width) << user_config << std::endl; +} + +/* Write configuration to config_path (set at initialization) */ void Configuration::write() { write(config_path); } -void Configuration::write(fs::path path) +/* Updates the auto refresher */ +void Configuration::update() { - std::ofstream output(path); - output << std::setw(tab_width) << game_config << std::endl; + auto_refresher.update(); } diff --git a/src/Configuration.hpp b/src/Configuration.hpp index 833927f..fc43b43 100644 --- a/src/Configuration.hpp +++ b/src/Configuration.hpp @@ -2,28 +2,41 @@ #define Configuration_h_ #include "json/json.hpp" - #include "filesystem.hpp" #include "Node.hpp" +#include "Animation.hpp" -struct Configuration : Node +class Configuration : public Node { - nlohmann::json sys_config, game_config, config; + +private: + + nlohmann::json sys_config, user_config; fs::path config_path; int tab_width = 4; + Animation auto_refresher = Animation(&Configuration::refresh, this); + fs::file_time_type config_file_modification_time; - Configuration(Node*); - Configuration(Node*, fs::path); void set_defaults(); - void load(); - void load(fs::path path); void merge(); - void write(); + +public: + + nlohmann::json config; + + Configuration(Node*, fs::path = "config.json"); + void load(fs::path path); + void load(); + void auto_refresh(bool); + void refresh(); void write(fs::path path); + void write(); + void update(); virtual std::string class_name() const { return "Configuration"; } }; +/* Extend GLM so nlohmann::json can read and write glm::vec2 */ namespace glm { template @@ -40,6 +53,7 @@ namespace glm } } +/* Extend std::filesystem so nlohmann::json can read and write std::filesystem::path */ #if defined(__MINGW32__) namespace std::experimental::filesystem #else diff --git a/src/Game.cpp b/src/Game.cpp index b9d882e..b2e4084 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -28,7 +28,7 @@ SDL_Surface* FramerateIndicator::get_surface() void FramerateIndicator::refresh() { - if (!is_hidden() && get_root()->bp_mono_font != NULL) + if (!is_hidden() && get_root()->bp_mono_font != nullptr) { unload(); SDL_Surface* surface = get_surface(); @@ -133,7 +133,7 @@ Game::Game() SDL_Log("initialized SDL ttf %d.%d.%d", SDL_TTF_MAJOR_VERSION, SDL_TTF_MINOR_VERSION, SDL_TTF_PATCHLEVEL); } - if ((bp_mono_font = TTF_OpenFont("BPmono.ttf", 14)) == NULL) + if ((bp_mono_font = TTF_OpenFont("BPmono.ttf", 14)) == nullptr) { print_error("Could not load BPmono.ttf"); } @@ -671,15 +671,13 @@ void Game::frame(float ticks) // std::cout << ", last_frame_length: " << last_frame_length << " [rendering frame]"; if (last_frame_length < 1000) { - // if (!is_gl_context) - // { - recorder.update(); - // } + recorder.update(); delegate.dispatch(); audio.update(); input.unsuppress_animation.update(); update(); framerate_indicator.update(); + configuration.update(); if (!is_gl_context) { SDL_SetRenderTarget(renderer, nullptr); diff --git a/src/Game.hpp b/src/Game.hpp index dd1fff9..99acf5b 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -57,6 +57,12 @@ private: public: + /* two-state enum equivalent to a boolean that can improve readability depending on the context */ + enum class Flip { + OFF, + ON + }; + /* Prevent an instance of this class from being copied or moved */ Game(const Game&) = delete; Game& operator=(const Game&) = delete; @@ -77,7 +83,7 @@ public: Input input = Input(this); Audio audio = Audio(this); std::vector frame_length_history; - TTF_Font* bp_mono_font = NULL; + TTF_Font* bp_mono_font = nullptr; FramerateIndicator framerate_indicator = FramerateIndicator(this); Game(); diff --git a/src/Segment.cpp b/src/Segment.cpp index 5fbc7ef..02fe6c7 100644 --- a/src/Segment.cpp +++ b/src/Segment.cpp @@ -15,7 +15,7 @@ Segment::Segment(const glm::vec2& location) : Segment(location, location) {}; Segment::Segment(const Box& start, const Box& end) : Segment(start.center(), end.center()) {}; -inline glm::vec2 Segment::start() const +glm::vec2 Segment::start() const { return start_; } diff --git a/src/Segment.hpp b/src/Segment.hpp index 10de75f..9c24cea 100644 --- a/src/Segment.hpp +++ b/src/Segment.hpp @@ -24,7 +24,7 @@ public: Segment(const glm::vec2&); Segment(const Box&, const Box&); Segment(const Sprite&, const Sprite&); - inline glm::vec2 start() const; + glm::vec2 start() const; void start(const glm::vec2&); glm::vec2 end() const; void end(const glm::vec2&); diff --git a/src/extension.cpp b/src/extension.cpp index 07683a1..fb02688 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -1,6 +1,7 @@ #include "Pixels.hpp" #include "extension.hpp" +/* Edit a vector in place, giving it the specified magnitude while maintaining the direction */ void sfw::set_magnitude(glm::vec2& vector, float magnitude) { vector = glm::normalize(vector) * magnitude; diff --git a/src/extension.hpp b/src/extension.hpp index c7d9de5..2e8aef8 100644 --- a/src/extension.hpp +++ b/src/extension.hpp @@ -40,7 +40,6 @@ namespace sfw glm::vec2 point_on_circle(float, float = 1.0f); void points_on_circle(std::vector&, int, float = 1.0f, const glm::vec2& = {0, 0}, float = 0.0f); std::vector points_on_circle(int, float = 1.0f, const glm::vec2& = {0, 0}, float = 0.0f); - glm::vec2 map_rectangle_xy_to_circle(const glm::vec2&, float = 1.0f, const glm::vec2& = {0, 0}); Box get_texture_box(SDL_Texture*); glm::vec2 fit_and_preserve_aspect(const glm::vec2&, const glm::vec2&); std::vector> get_blinds_boxes(glm::vec2, float = 0.05f, int = 4);