commit 4bf9b6cf9de33daec1944f9f2e58a214865553b4 Author: frank <420@shampoo.ooo> Date: Thu Jun 16 15:24:14 2022 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6460a2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +index.* +local/ +*.o +BPmono.ttf +pepy +compile_commands.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ad2d0b1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/sb"] + path = lib/sb + url = makar:/var/www/git/sb diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4a8e1c3 --- /dev/null +++ b/Makefile @@ -0,0 +1,192 @@ +# Pepy by @ohsqueezy [ohsqueezy.itch.io] + +####################### +# Location parameters # +####################### + +# Location of project specific source files +SRC_DIR := src/ + +# 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 := lib/sb/ +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 := $(HOME)/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 # +############################# + +SDL_CFLAGS = $(shell $(SDLCONFIG) --cflags) +SDL_LFLAGS := $(shell $(SDLCONFIG) --libs) +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 project 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 Log.hpp) +$(SB_SRC_DIR)Node.o : $(addprefix $(SB_SRC_DIR),Game.hpp Configuration.hpp Delegate.hpp Display.hpp Input.hpp Box.hpp Audio.hpp Log.hpp) +$(SB_SRC_DIR)Sprite.o : $(addprefix $(SB_SRC_DIR),Node.hpp Game.hpp Box.hpp Animation.hpp Color.hpp extension.hpp Pixels.hpp Log.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 Log.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 Log.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 Log.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 Log.hpp utility.hpp) +$(SB_SRC_DIR)Audio.o : $(addprefix $(SB_SRC_DIR),Node.hpp Display.hpp Configuration.hpp Box.hpp filesystem.hpp extension.hpp) +$(SB_SRC_DIR)GLObject.o : $(addprefix $(SB_SRC_DIR),Log.hpp) +$(SB_SRC_DIR)Texture.o : $(addprefix $(SB_SRC_DIR),GLObject.hpp filesystem.hpp Log.hpp) +$(SB_SRC_DIR)VBO.o : $(addprefix $(SB_SRC_DIR),Log.hpp GLObject.hpp Attributes.hpp extension.hpp) +$(SB_SRC_DIR)Attributes.o : $(addprefix $(SB_SRC_DIR),Log.hpp extension.hpp) +$(SRC_DIR)Pepy.o : $(SRC_H_FILES) $(SB_H_FILES) +%.o : %.cpp %.hpp + $(CPPC) $(CPP_FLAGS) $< -c -o $@ + +############### +# Linux build # +############### + +linux : CC = clang +linux : CPPC = clang++ +linux : CFLAGS = -g -Wall -Wextra -O0 -c -I$(SB_LIB_DIR) -I$(SB_SRC_DIR) $(SDL_CFLAGS) +linux : CPP_FLAGS = $(CFLAGS) --std=c++17 +linux : LFLAGS = $(SDL_LFLAGS) -lpthread -lGL -lGLESv2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs +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__ $^ -o pepy + +############# +# Web build # +############# + +# Use Emscripten to output JavaScript and an HTML index page for running in the browser + +EMSCRIPTENHOME = $(HOME)/ext/software/emsdk/upstream/emscripten +EMSCRIPTEN_CFLAGS = -O3 -Wall -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="['png']" -s USE_SDL_TTF=2 \ + -s USE_SDL_MIXER=2 -s SDL2_MIXER_FORMATS="['ogg','mp3']" -s USE_OGG=1 -s USE_VORBIS=1 -s USE_MPG123=1 \ + -s USE_MODPLUG=1 --no-heap-copy -I$(SB_LIB_DIR) -I$(SB_SRC_DIR) +EMSCRIPTEN_LFLAGS = -s MIN_WEBGL_VERSION=2 -s EXPORTED_FUNCTIONS="['_main']" -s ALLOW_MEMORY_GROWTH=1 -s FULL_ES3=1 --shell-file shell.html +EMSCRIPTEN_PRELOADS = --preload-file "BPmono.ttf"@/ --preload-file "config.json"@/ --preload-file "resource/"@/"resource/" \ + --preload-file "src/shader.vert"@/"src/" --preload-file "src/shader.frag"@/"src/" + +emscripten : CC = $(EMSCRIPTENHOME)/emcc +emscripten : CPPC = $(EMSCRIPTENHOME)/em++ +emscripten : CFLAGS = $(EMSCRIPTEN_CFLAGS) +emscripten : CPP_FLAGS = $(CFLAGS) --std=c++17 +emscripten : $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) $(SB_O_FILES) $(SRC_O_FILES) + $(CREATE_FONT_SYMLINK) + $(CPPC) $(CPP_FLAGS) $(EMSCRIPTEN_LFLAGS) $(EMSCRIPTEN_PRELOADS) $^ -o "index.html" + +######################### +# Clean up object files # +######################### + +clean : + -rm $(SRC_DIR)*.o + +clean-all : + -find . -iname "*.o" -exec rm {} \; + +############# +# compiledb # +############# + +# Generate a clang JSON compilation database file. This can be useful for example for code completion. It requires a +# compiledb binary (https://pypi.org/project/compiledb/). It should be generated manually every time a file is added, +# renamed, or the compilation flags change. The generated database is based on the Linux build. + +PATH_TO_COMPILEDB = $(HOME)/.local/bin/compiledb +compiledb : + -$(PATH_TO_COMPILEDB) -n make linux -k + +##################### +# cross compilation # +##################### + +# WARNING: untested + +# Below assignments and targets are necessary for cross-compiling to Android, web (via emscripten), Windows (via mingw), +# and OS/X, but cross-compilation targets have not been tested in a while and won't compile without significant changes. + +BUILDDIR := build +SDLHOME := $(EXT_SRC_ROOT)/SDL2-2.0.14 +SDL_IMG_HOME := $(EXT_SRC_ROOT)/SDL2_image-2.0.4 +SDL_TTF_HOME := $(EXT_SRC_ROOT)/SDL2_ttf-2.0.14 +GLEW_WIN32_HOME = $(EXT_SRC_ROOT)/glew-2.1.0-win32 +PROJECTHOME = $(shell pwd) +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 + +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/config.json b/config.json new file mode 100644 index 0000000..dc6cc3f --- /dev/null +++ b/config.json @@ -0,0 +1,38 @@ +{ + "display": + { + "dimensions": [960, 540], + "framerate": 60, + "title": "Pepy", + "debug": false, + "render driver": "opengles3", + "show-cursor": true + }, + "configuration": + { + "auto-refresh": true + }, + "keys": + { + }, + "input": + { + "any-key-ignore-commands": ["up", "right", "down", "left"], + "suppress-any-key-on-mods": true + }, + "log": + { + "enabled": true, + "output-directory": "/var/log/sb/", + "debug-to-stdout": false, + "debug-to-file": true, + "info-file-name": "cuckoo_info.log", + "debug-file-name": "cuckoo_debug.log" + }, + "sim": + { + "wall-count": 48, + "ball-scale": 0.15, + "spawn-radius": 0.35 + } +} diff --git a/lib/sb b/lib/sb new file mode 160000 index 0000000..ee119ec --- /dev/null +++ b/lib/sb @@ -0,0 +1 @@ +Subproject commit ee119ecc7e4287838cec5aac127820d4738de17f diff --git a/resource/Book_of_Pepy/1071003012.jpg b/resource/Book_of_Pepy/1071003012.jpg new file mode 100644 index 0000000..283d388 Binary files /dev/null and b/resource/Book_of_Pepy/1071003012.jpg differ diff --git a/resource/Book_of_Pepy/129548486_1656033331242674_6748215393958512364_n.jpg b/resource/Book_of_Pepy/129548486_1656033331242674_6748215393958512364_n.jpg new file mode 100644 index 0000000..ff6e6fe Binary files /dev/null and b/resource/Book_of_Pepy/129548486_1656033331242674_6748215393958512364_n.jpg differ diff --git a/resource/Book_of_Pepy/1gfxra.jpg b/resource/Book_of_Pepy/1gfxra.jpg new file mode 100644 index 0000000..f9cc131 Binary files /dev/null and b/resource/Book_of_Pepy/1gfxra.jpg differ diff --git a/resource/Book_of_Pepy/E6IhBxRXIAETJ29.jpeg b/resource/Book_of_Pepy/E6IhBxRXIAETJ29.jpeg new file mode 100644 index 0000000..da13d23 Binary files /dev/null and b/resource/Book_of_Pepy/E6IhBxRXIAETJ29.jpeg differ diff --git a/resource/Book_of_Pepy/E9GrqOlWYAE2Sgw.jpeg b/resource/Book_of_Pepy/E9GrqOlWYAE2Sgw.jpeg new file mode 100644 index 0000000..9531016 Binary files /dev/null and b/resource/Book_of_Pepy/E9GrqOlWYAE2Sgw.jpeg differ diff --git a/resource/Book_of_Pepy/Ea1zDCEUwAAKUcF.jpeg b/resource/Book_of_Pepy/Ea1zDCEUwAAKUcF.jpeg new file mode 100644 index 0000000..2e653ae Binary files /dev/null and b/resource/Book_of_Pepy/Ea1zDCEUwAAKUcF.jpeg differ diff --git a/resource/Book_of_Pepy/EcrFXg5VcAE6tK6.jpeg b/resource/Book_of_Pepy/EcrFXg5VcAE6tK6.jpeg new file mode 100644 index 0000000..2861dce Binary files /dev/null and b/resource/Book_of_Pepy/EcrFXg5VcAE6tK6.jpeg differ diff --git a/resource/Book_of_Pepy/Japanese-chocolate-whisky-Morinaga-twig-big-Koeda-whisky-pairing-buy-special-drink-review-photos-4.webp b/resource/Book_of_Pepy/Japanese-chocolate-whisky-Morinaga-twig-big-Koeda-whisky-pairing-buy-special-drink-review-photos-4.webp new file mode 100644 index 0000000..cfeedbe Binary files /dev/null and b/resource/Book_of_Pepy/Japanese-chocolate-whisky-Morinaga-twig-big-Koeda-whisky-pairing-buy-special-drink-review-photos-4.webp differ diff --git a/resource/Book_of_Pepy/Lenny-Face.png b/resource/Book_of_Pepy/Lenny-Face.png new file mode 100644 index 0000000..b11e830 Binary files /dev/null and b/resource/Book_of_Pepy/Lenny-Face.png differ diff --git a/resource/Book_of_Pepy/b89d70e6fb9c4caaed2e797f01bb7575.jpg b/resource/Book_of_Pepy/b89d70e6fb9c4caaed2e797f01bb7575.jpg new file mode 100644 index 0000000..5479c01 Binary files /dev/null and b/resource/Book_of_Pepy/b89d70e6fb9c4caaed2e797f01bb7575.jpg differ diff --git a/resource/Book_of_Pepy/cat-party-hat-eats-fish-burger-beige-multi-colored-eating-big-fresh-white-square-plate-drinking-beer-table-183867588.jpg b/resource/Book_of_Pepy/cat-party-hat-eats-fish-burger-beige-multi-colored-eating-big-fresh-white-square-plate-drinking-beer-table-183867588.jpg new file mode 100644 index 0000000..bbb2575 Binary files /dev/null and b/resource/Book_of_Pepy/cat-party-hat-eats-fish-burger-beige-multi-colored-eating-big-fresh-white-square-plate-drinking-beer-table-183867588.jpg differ diff --git a/resource/Book_of_Pepy/large.webp b/resource/Book_of_Pepy/large.webp new file mode 100644 index 0000000..dca0334 Binary files /dev/null and b/resource/Book_of_Pepy/large.webp differ diff --git a/resource/Book_of_Pepy/necropolis_of_Pepy.png b/resource/Book_of_Pepy/necropolis_of_Pepy.png new file mode 100644 index 0000000..e798830 Binary files /dev/null and b/resource/Book_of_Pepy/necropolis_of_Pepy.png differ diff --git a/resource/Book_of_Pepy/vz-ff8724ba-584e-4f9d-bae5-f7c15e462512.jpeg b/resource/Book_of_Pepy/vz-ff8724ba-584e-4f9d-bae5-f7c15e462512.jpeg new file mode 100644 index 0000000..9031c62 Binary files /dev/null and b/resource/Book_of_Pepy/vz-ff8724ba-584e-4f9d-bae5-f7c15e462512.jpeg differ diff --git a/resource/Book_of_Pepy/walking_pepy_london.png b/resource/Book_of_Pepy/walking_pepy_london.png new file mode 100644 index 0000000..50aef8a Binary files /dev/null and b/resource/Book_of_Pepy/walking_pepy_london.png differ diff --git a/resource/space.png b/resource/space.png new file mode 100644 index 0000000..e97dbea Binary files /dev/null and b/resource/space.png differ diff --git a/resource/wad.png b/resource/wad.png new file mode 100644 index 0000000..e12c550 Binary files /dev/null and b/resource/wad.png differ diff --git a/shell.html b/shell.html new file mode 100644 index 0000000..922c103 --- /dev/null +++ b/shell.html @@ -0,0 +1,180 @@ + + + + + + B.U.D.D.I. + + + +
+ Pepy, it's cuckoo time! Please make it as big as possible using the arrow keys in + 10 seconds. +
+
+
+
+
Downloading...
+
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + {{{ SCRIPT }}} + + diff --git a/src/Model.cpp b/src/Model.cpp new file mode 100644 index 0000000..eaf8028 --- /dev/null +++ b/src/Model.cpp @@ -0,0 +1,160 @@ +/* Cuckoo by @ohsqueezy [ohsqueezy.itch.io] */ + +#include "Model.hpp" + +/* Default constructor for Model */ +Model::Model() {}; + +/* Construct a Model, adding Attributes each already wrapped in a shared pointer. The attributes should + * be passed as a map with each key being a name and each value being a shared pointer to attributes. */ +Model::Model(const std::map>& attributes_pack) +{ + for (auto attributes : attributes_pack) + { + this->attributes(attributes.second, attributes.first); + } +} + +/* Construct a Model, adding Attributes, which will each be wrapped in a shared pointer and stored in the + * created object. The attributes should be passed as a map with each key being a name and each value being + * an attributes object. */ +Model::Model(const std::map& attributes_pack) +{ + for (auto attributes : attributes_pack) + { + this->attributes(attributes.second, attributes.first); + } +} + +/* Construct a new model object by passing a list of names which will be used to initialize + * empty attributes objects with the given names */ +Model::Model(const std::initializer_list& names) +{ + for (const std::string& name : names) + { + this->attributes(sb::Attributes(), name); + } +} + +/* Get the entire map of attributes, each wrapped in its shared pointer held by this object. + * Can be used to iterate through the attributes. */ +std::map>& Model::attributes() +{ + return model_attributes; +} + +/* Get the attributes under name, wrapped in the shared pointer held by this object. This + * function uses the at method of std::map, so name must refer to attributes already + * stored in this model. Use this function to share ownership of the attributes or to gain + * access to the public interface of the attributes. */ +std::shared_ptr& Model::attributes(const std::string& name) +{ + return attributes().at(name); +} + +/* Get the attributes under name, wrapped in the shared pointer held by this object. This + * function uses operator[] or std::map, so this can be used to add new attributes to the + * object if they are wrapped in a shared pointer. */ +std::shared_ptr& Model::operator[](const std::string& name) +{ + auto element = attributes().find(name); + /* add an empty Attributes at name if it doesn't exist yet */ + if (element == attributes().end()) + { + attributes(sb::Attributes{}, name); + } + return attributes()[name]; +} + +/* Assign name to attributes, copy and wrap in a shared pointer. The model can share + * ownership of the created attribute memory with callers that request it. */ +void Model::attributes(const sb::Attributes& attributes, const std::string& name) +{ + this->attributes(std::make_shared(attributes), name); +} + +/* Assign name to attributes and share ownership. */ +void Model::attributes(const std::shared_ptr& attributes, const std::string& name) +{ + this->attributes()[name] = attributes; +} + +/* Enable all attributes. */ +void Model::enable() +{ + for (const auto& attributes : this->attributes()) + { + attributes.second->enable(); + } +} + +/* Disable all attributes. */ +void Model::disable() +{ + for (const auto& attributes : this->attributes()) + { + attributes.second->disable(); + } +} + +/* Return a reference to the texture container. */ +std::map& Model::textures() +{ + return model_textures; +} + +/* Get the texture at name. This can be used to read the texture memory, share ownership of it, or + * anything else a Texture object can be used for with direct calls to GL functions. */ +sb::Texture& Model::texture(const std::string& name) +{ + return textures().at(name); +} + +/* Get the default texture. The default texture must have previously been set with the default key as + * the name, which can be done using Model::texture(sb::Texture). */ +sb::Texture& Model::texture() +{ + return texture(DEFAULT_TEXTURE_NAME); +} + +/* Assign name to texture and share ownership. */ +void Model::texture(const sb::Texture& texture, const std::string& name) +{ + textures()[name] = texture; +} + +/* If no name is specified, use the default texture. This can be used to conveniently setup a model + * with only one texture. */ +void Model::texture(const sb::Texture& texture) +{ + this->texture(texture, DEFAULT_TEXTURE_NAME); +} + +/* Get the model's transformation matrix. */ +const glm::mat4& Model::transformation() const +{ + return model_transformation; +} + +/* Set the model's transformation matrix. */ +void Model::transformation(const glm::mat4& transformation) +{ + model_transformation = transformation; +} + +/* Return the size in bytes of the sum of the attributes. */ +std::size_t Model::size() +{ + std::size_t sum = 0; + for (const auto& attributes : this->attributes()) + { + sum += attributes.second->size(); + } + return sum; +} + +/* Return the transformation matrix. */ +Model::operator glm::mat4() const +{ + return model_transformation; +} diff --git a/src/Model.hpp b/src/Model.hpp new file mode 100644 index 0000000..5145e60 --- /dev/null +++ b/src/Model.hpp @@ -0,0 +1,75 @@ +/* Cuckoo by @ohsqueezy [ohsqueezy.itch.io] */ + +#ifndef MODEL_H_ +#define MODEL_H_ + +/* GL functions */ +#if defined(__EMSCRIPTEN__) +#include +#else +#include "glew/glew.h" +#endif + +#include +#include +#include +#include +#include +#include "glm/glm.hpp" +#include "Attributes.hpp" +#include "Texture.hpp" + +class Model +{ + +private: + + inline static const std::string DEFAULT_TEXTURE_NAME = "default"; + std::map model_textures; + std::map> model_attributes; + glm::mat4 model_transformation {1.0f}; + +public: + + Model(); + Model(const std::map>&); + Model(const std::map&); + Model(const std::initializer_list&); + std::map>& attributes(); + std::shared_ptr& attributes(const std::string&); + void attributes(const sb::Attributes&, const std::string&); + void attributes(const std::shared_ptr&, const std::string&); + std::shared_ptr& operator[](const std::string&); + void enable(); + void disable(); + std::map& textures(); + sb::Texture& texture(const std::string&); + sb::Texture& texture(); + void texture(const sb::Texture&, const std::string&); + void texture(const sb::Texture&); + const glm::mat4& transformation() const; + void transformation(const glm::mat4&); + std::size_t size(); + operator glm::mat4() const; + +}; + +class Plane : public Model +{ + +public: + + inline const static std::shared_ptr position = std::make_shared(sb::Attributes{ + {-1.0f, 1.0f}, {1.0f, 1.0f}, {-1.0f, -1.0f}, + {1.0f, 1.0f}, {1.0f, -1.0f}, {-1.0f, -1.0f} + }); + inline const static std::shared_ptr uv = std::make_shared(sb::Attributes{ + {0.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f}, + {1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f} + }); + + Plane() : Model(std::map>({{"position", position}, {"uv", uv}})) {} + +}; + +#endif diff --git a/src/Pepy.cpp b/src/Pepy.cpp new file mode 100644 index 0000000..960bd76 --- /dev/null +++ b/src/Pepy.cpp @@ -0,0 +1,383 @@ +/* Pepy by @ohsqueezy [ohsqueezy.itch.io] */ + +#include "Pepy.hpp" + +/* Launch the Pepy instance's mainloop */ +int main() +{ + Pepy pepy = Pepy(); + pepy.run(); + pepy.quit(); + return 0; +} + +/* Initialize a Pepy instance */ +Pepy::Pepy() +{ + /* subscribe to command events */ + get_delegate().subscribe(&Pepy::respond, this); + get_delegate().subscribe(&Pepy::respond, this, SDL_MOUSEBUTTONDOWN); + get_delegate().subscribe(&Pepy::respond, this, SDL_MOUSEBUTTONUP); + /* create a glowing ring */ + int point_count = configuration()["sim"]["wall-count"].get() + 1; + std::vector outer_points = sb::points_on_circle(point_count, 0.75f, {0.0f, 0.0f}); + std::vector inner_points = sb::points_on_circle(point_count, 0.65f, {0.0f, 0.0f}); + float inner_saturation = 0.1f; + float inner_value = 1.0f; + float outer_saturation = 1.0f; + float outer_value = 0.1f; + int next; + for (int ii = 0; ii <= point_count; ii++) + { + next = (ii + 1) % point_count; + cuckoo["position"]->add(outer_points[ii]); + cuckoo["position"]->add(outer_points[next]); + cuckoo["position"]->add(inner_points[ii]); + cuckoo["position"]->add(inner_points[ii]); + cuckoo["position"]->add(inner_points[next]); + cuckoo["position"]->add(outer_points[next]); + cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast(point_count) * 255.0f, outer_saturation, outer_value))); + cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast(point_count) * 255.0f, outer_saturation, outer_value))); + cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast(point_count) * 255.0f, inner_saturation, inner_value))); + cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast(point_count) * 255.0f, inner_saturation, inner_value))); + cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast(point_count) * 255.0f, inner_saturation, inner_value))); + cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast(point_count) * 255.0f, outer_saturation, outer_value))); + } + /* create balls */ + float size = configuration()["sim"]["ball-scale"]; + auto spawn_points = sb::points_on_circle(5, configuration()["sim"]["spawn-radius"]); + for (auto spawn_point : spawn_points) + { + Plane ball; + ball.transformation(glm::translate(glm::vec3{spawn_point.x, spawn_point.y, 1.0f}) * glm::scale(glm::vec3{size, size, 1.0f})); + wad.push_back(std::pair{ball, glm::vec2{0.0f, 0.0f}}); + } + background.transformation(glm::scale(glm::vec3{5.0f, 5.0f, 1.0f})); + /* load Open GL */ + load_gl_context(); + /* load wad textures */ + sb::Texture wad_texture {"resource/wad.png"}; + wad_texture.load(); + /* Apply the wad texture to each wad */ + for (size_t wad_ii = 0; wad_ii < wad.size(); wad_ii++) + { + wad[wad_ii].first.texture(wad_texture); + } + sb::Texture texture {"resource/space.png"}; + texture.load(); + background.texture(texture); +} + +/* Create GL context via super class and load vertices, UV data, and shaders */ +void Pepy::load_gl_context() +{ + super::load_gl_context(); + cuckoo_vao.generate(); + wad_vao.generate(); + cuckoo_vao.bind(); + /* Generate ID for the vertex buffer object that will hold vertex data. Because there is one buffer, data + * will be copied in one after the other, offset to after the previous location. */ + vbo.generate(); + vbo.bind(); + vbo.allocate(cuckoo.size() + wad[0].first.size(), GL_STATIC_DRAW); + GLuint vertex_shader = load_shader("src/shader.vert", GL_VERTEX_SHADER); + GLuint fragment_shader = load_shader("src/shader.frag", GL_FRAGMENT_SHADER); + shader = glCreateProgram(); + glAttachShader(shader, vertex_shader); + glAttachShader(shader, fragment_shader); + sb::Log::gl_errors("after attaching shaders"); + cuckoo.attributes("position")->bind(0, shader, "vertex_position"); + cuckoo.attributes("color")->bind(1, shader, "vertex_color"); + vbo.add(*cuckoo.attributes("position")); + vbo.add(*cuckoo.attributes("color")); + wad_vao.bind(); + wad[0].first.attributes("position")->bind(0, shader, "vertex_position"); + wad[0].first.attributes("uv")->bind(2, shader, "vertex_uv"); + vbo.add(*wad[0].first.attributes("position")); + vbo.add(*wad[0].first.attributes("uv")); + sb::Log::gl_errors("after VBO allocation"); + link_shader(shader); + uniform["blend"] = glGetUniformLocation(shader, "blend_min_hsv"); + uniform["orthographic_projection"] = glGetUniformLocation(shader, "orthographic_projection"); + uniform["model_transformation"] = glGetUniformLocation(shader, "model_transformation"); + uniform["base_texture"] = glGetUniformLocation(shader, "base_texture"); + uniform["textured"] = glGetUniformLocation(shader, "textured"); + uniform["scroll"] = glGetUniformLocation(shader, "scroll"); + uniform["time"] = glGetUniformLocation(shader, "time"); + sb::Log::gl_errors("after uniforms"); + /* enable alpha rendering */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + sb::Log::gl_errors("at end of load context"); +} + +void Pepy::respond(SDL_Event& event) +{ + /* Will check if reset should be triggered */ + bool reset = false; + /* Check for any direction when waiting to reset */ + if (stopped && delegate.compare(event, {"up", "down", "left", "right"})) + { + reset = true; + } + if (event.type == SDL_MOUSEBUTTONDOWN) + { + if (shaking) + { + grabbed = true; + } + /* Mouse button will trigger reset when stopped */ + else if (stopped) + { + reset = true; + } + } + else if (event.type == SDL_MOUSEBUTTONUP && shaking) + { + grabbed = false; + } + /* Reset to countdown */ + if (reset) + { + float size = configuration()["sim"]["ball-scale"]; + auto spawn_points = sb::points_on_circle(5, configuration()["sim"]["spawn-radius"]); + int ii = 0; + for (auto& ball : wad) + { + ball.first.transformation(glm::translate(glm::vec3{spawn_points[ii].x, spawn_points[ii++].y, 1.0f}) * glm::scale(glm::vec3{size, size, 1.0f})); + } + stopped = false; + shaking = true; + } +} + +/* Update state and draw the screen */ +void Pepy::update() +{ + /* number of seconds running */ + time_seconds = SDL_GetTicks() / 1000.0f; + if (shaking) + { + countdown -= last_frame_length / 1000.0f; + if (countdown < 0.0f) + { + shaking = false; + flying = true; + countdown = 10.0f; + } + } + /* move cuckoo with mouse */ + if (grabbed && shaking) + { + /* get mouse coordinates in NDC */ + SDL_GetMouseState(&mouse_pixel.x, &mouse_pixel.y); + glm::vec2 mouse_ndc { + static_cast(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f, + (1.0f - static_cast(mouse_pixel.y) / window_box().height()) * 2.0f - 1.0f + }; + cuckoo_offset.x += weight(mouse_ndc.x / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.x), 1.0f), 0.1f); + cuckoo_offset.y += weight(mouse_ndc.y / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.y), 1.0f), 0.1f); + } + else + { + /* move cuckoo with keys */ + const std::uint8_t* state = SDL_GetKeyboardState(nullptr); + float motion = weight(1 / 25.0f), diagonal = motion * std::sin(glm::pi() * 0.25f); + if (state[SDL_SCANCODE_UP]) + { + if (state[SDL_SCANCODE_RIGHT]) + { + cuckoo_offset.x += diagonal; + cuckoo_offset.y += diagonal; + } + else if (state[SDL_SCANCODE_LEFT]) + { + cuckoo_offset.x -= diagonal; + cuckoo_offset.y += diagonal; + } + else + { + cuckoo_offset.y += motion; + } + } + else if (state[SDL_SCANCODE_RIGHT]) + { + if (state[SDL_SCANCODE_DOWN]) + { + cuckoo_offset.x += diagonal; + cuckoo_offset.y -= diagonal; + } + else + { + cuckoo_offset.x += motion; + } + } + else if (state[SDL_SCANCODE_DOWN]) + { + if (state[SDL_SCANCODE_LEFT]) + { + cuckoo_offset.x -= diagonal; + cuckoo_offset.y -= diagonal; + } + else + { + cuckoo_offset.y -= motion; + } + } + else if (state[SDL_SCANCODE_LEFT]) + { + cuckoo_offset.x -= motion; + } + } + cuckoo_offset.x -= weight(glm::sign(cuckoo_offset).x * return_speed); + cuckoo_offset.y -= weight(glm::sign(cuckoo_offset).y * return_speed); + cuckoo.transformation(glm::translate(cuckoo_offset)); + hue_offset += weight(0.002f); + glm::vec2 x_range = {-1.0f, 1.0f}; + glm::vec2 y_range = {-1.0f, 1.0f}; + /* move wad */ + bool all_stopped = true; + for (auto& ball : wad) + { + const glm::vec2& ball_center = {ball.first.transformation()[3].x, ball.first.transformation()[3].y}; + const glm::vec2& cuckoo_center = {cuckoo.transformation()[3].x, cuckoo.transformation()[3].y}; + float distance = glm::distance(ball_center, cuckoo_center); + if (shaking) + { + if (distance > 0.65f) + { + float angle = glm::atan(cuckoo_center.y - ball_center.y, cuckoo_center.x - ball_center.x); + ball.second.y += 0.0005f; + ball.second.x = angle + glm::half_pi(); + } + } + glm::vec2 step {glm::sin(ball.second.x) * ball.second.y, -glm::cos(ball.second.x) * ball.second.y}; + ball.first.transformation(glm::translate(glm::vec3{step.x, step.y, 0.0f}) * ball.first.transformation()); + if (ball.second.y > 0) + { + float friction; + if (shaking) + { + friction = 0.00005f; + } + else + { + friction = 0.0001f; + } + ball.second.y = std::max(0.0f, ball.second.y - friction); + } + if (ball_center.x < x_range[0]) + { + x_range[0] = ball_center.x - 0.1f; + } + else if (ball_center.x > x_range[1]) + { + x_range[1] = ball_center.x + 0.1f; + } + if (ball_center.y < y_range[0]) + { + y_range[0] = ball_center.y - 0.1f; + } + else if (ball_center.y > y_range[1]) + { + y_range[1] = ball_center.y + 0.1f; + } + if (ball.second.y > 0.0f) + { + all_stopped = false; + } + } + if (all_stopped && flying) + { + flying = false; + stopped = true; + glm::vec2 sum {0.0f, 0.0f}; + std::vector centers; + for (auto& ball : wad) + { + const glm::vec2& ball_center = {ball.first.transformation()[3].x, ball.first.transformation()[3].y}; + centers.push_back(ball_center); + sum += ball_center; + } + glm::vec2 centroid = sum / wad.size(); + angle_sort sorter {centroid, centers[0]}; + std::sort(centers.begin(), centers.end(), sorter); + float area = 0.0f; + for (std::size_t center_ii = 0; center_ii < centers.size() - 1; center_ii++) + { + area += centers[center_ii].x * centers[center_ii + 1].y - centers[center_ii + 1].x * centers[center_ii].y; + } + area += centers[centers.size() - 1].x * centers[0].y - centers[0].x * centers[centers.size() - 1].y; + area = std::abs(area) / 2.0f; + std::cout << "Your score (size) is " << area << ". Press arrow key to play again." << std::endl; + } + /* paint over screen */ + glClearColor(0, 0, 0, 1); + sb::Log::gl_errors("after setting clear color"); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + sb::Log::gl_errors("after clearing screen"); + glUseProgram(shader); + sb::Log::gl_errors("after using program"); + /* set orthographic project for viewing entire scene at normalized screen ratio */ + aspect_ratio = window_box().aspect(true); + if (window_box().width() > window_box().height()) + { + orthographic_projection = glm::ortho(aspect_ratio * x_range[0], aspect_ratio * x_range[1], y_range[0], y_range[1]); + } + else + { + orthographic_projection = glm::ortho(x_range[0], x_range[1], aspect_ratio * y_range[0], aspect_ratio * y_range[1]); + } + glUniformMatrix4fv(uniform["orthographic_projection"], 1, GL_FALSE, &orthographic_projection[0][0]); + sb::Log::gl_errors("after setting orthographic projection"); + /* draw background */ + wad_vao.bind(); + glUniform3f(uniform["blend"], hue_offset, 0.0f, 1.0f); + glUniform1i(uniform["textured"], true); + glUniform1i(uniform["scroll"], true); + glUniform1f(uniform["time"], time_seconds); + glUniform1i(uniform["base_texture"], 0); + glActiveTexture(GL_TEXTURE0); + background.texture().bind(); + glUniformMatrix4fv(uniform["model_transformation"], 1, GL_FALSE, reinterpret_cast(&background.transformation()[0][0])); + background.enable(); + glDrawArrays(GL_TRIANGLES, 0, background.attributes("position")->count()); + background.disable(); + /* draw wad */ + wad_vao.bind(); + glUniform3f(uniform["blend"], 0.0f, 0.0f, 1.0f); + glUniform1i(uniform["textured"], true); + glUniform1i(uniform["scroll"], false); + sb::Log::gl_errors("after setting blending and textured flag"); + for (std::pair& ball : wad) + { + glUniform1i(uniform["base_texture"], 0); + sb::Log::gl_errors("after setting texture uniform"); + glActiveTexture(GL_TEXTURE0); + sb::Log::gl_errors("after activating texture"); + ball.first.texture().bind(); + sb::Log::gl_errors("after binding wad"); + glUniformMatrix4fv(uniform["model_transformation"], 1, GL_FALSE, reinterpret_cast(&ball.first.transformation()[0][0])); + ball.first.enable(); + glDrawArrays(GL_TRIANGLES, 0, ball.first.attributes("position")->count()); + ball.first.disable(); + sb::Log::gl_errors("after drawing wad"); + } + /* draw cuckoo */ + if (shaking || stopped) + { + cuckoo_vao.bind(); + glUniform3f(uniform["blend"], hue_offset, 0.5f, 1.0f); + glUniform1i(uniform["textured"], false); + glUniformMatrix4fv(uniform["model_transformation"], 1, GL_FALSE, reinterpret_cast(&cuckoo.transformation()[0][0])); + cuckoo.enable(); + glDrawArrays(GL_TRIANGLES, 0, cuckoo.attributes("position")->count()); + cuckoo.disable(); + } + SDL_GL_SwapWindow(window()); + sb::Log::gl_errors("at end of update"); + if (shaking) + { + std::cout << countdown << std::endl; + } +} diff --git a/src/Pepy.hpp b/src/Pepy.hpp new file mode 100644 index 0000000..7010756 --- /dev/null +++ b/src/Pepy.hpp @@ -0,0 +1,85 @@ +/* Pepy by @ohsqueezy [ohsqueezy.itch.io] */ + +#ifndef PEPY_H_ +#define PEPY_H_ + +/* Enable swizzling in GLM */ +#define GLM_ENABLE_EXPERIMENTAL +#include +#include + +/* [SPACE BOX] headers */ +#include "Game.hpp" +#include "VBO.hpp" + +/* Project headers */ +#include "Model.hpp" + +class angle_sort +{ + glm::vec2 m_origin; + glm::vec2 m_dreference; + + // z-coordinate of cross-product, aka determinant + static double xp(glm::vec2 a, glm::vec2 b) { return a.x * b.y - a.y * b.x; } + +public: + + angle_sort(const glm::vec2 origin, const glm::vec2 reference) : m_origin(origin), m_dreference(reference - origin) {} + + bool operator()(const glm::vec2 a, const glm::vec2 b) const + { + const glm::vec2 da = a - m_origin, db = b - m_origin; + const double detb = xp(m_dreference, db); + + // nothing is less than zero degrees + if (detb == 0 && db.x * m_dreference.x + db.y * m_dreference.y >= 0) return false; + + const double deta = xp(m_dreference, da); + + // zero degrees is less than anything else + if (deta == 0 && da.x * m_dreference.x + da.y * m_dreference.y >= 0) return true; + + if (deta * detb >= 0) { + // both on same side of reference, compare to each other + return xp(da, db) > 0; + } + + // vectors "less than" zero degrees are actually large, near 2 pi + return deta > 0; + } +}; + +class Pepy : public Game +{ + +private: + + /* Convention for calling parent class in a consistent way across classes */ + typedef Game super; + + bool shaking = true, flying = false, stopped = false; + sb::VAO cuckoo_vao, wad_vao; + sb::VBO vbo; + GLuint shader; + Model cuckoo; + std::vector> wad; + std::map uniform; + float hue_offset = 0.0f, time_seconds = 0.0f, aspect_ratio = 1.0f, return_speed = 0.0075f, cuckoo_speed = 0.0f, countdown = 10.0f; + glm::mat4 orthographic_projection {1}; + glm::vec3 cuckoo_offset {0.0f, 0.0f, 0.0f}; + bool grabbed = false; + glm::ivec2 mouse_pixel = {0, 0}; + Plane background; + + void load_gl_context(); + void update(); + +public: + + Pepy(); + void respond(SDL_Event&); + +}; + +#endif diff --git a/src/shader.frag b/src/shader.frag new file mode 100644 index 0000000..c80c969 --- /dev/null +++ b/src/shader.frag @@ -0,0 +1,44 @@ +#version 300 es + +/* Pepy by @ohsqueezy [ohsqueezy.itch.io] */ + +precision mediump float; +in vec2 uv; +in vec3 color; +uniform sampler2D base_texture; +uniform vec3 blend_min_hsv; +uniform float time; +uniform bool scroll; +uniform bool textured; +out vec4 myOutputColor; + +/* from http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl, licensed under WTFPL */ +vec3 hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void main(void) +{ + if (textured) + { + // if (scroll) + // { + // ivec2 texture_size = textureSize(base_texture, 0); + // float speed = time * 35.0; + // gl_FragColor = texelFetch(base_texture, ivec2(mod(vec2(gl_FragCoord.x + speed, gl_FragCoord.y - speed), texture_size)), 0); + // } + // else + // { + myOutputColor = texture(base_texture, uv); + // } + } + else + { + myOutputColor = vec4(color, 1.0); + } + /* apply blending, leaving alpha unchanged */ + myOutputColor.xyz = min(myOutputColor.xyz, hsv2rgb(blend_min_hsv)); +} diff --git a/src/shader.vert b/src/shader.vert new file mode 100644 index 0000000..e75eb1a --- /dev/null +++ b/src/shader.vert @@ -0,0 +1,19 @@ +#version 300 es + +/* Pepy by @ohsqueezy [ohsqueezy.itch.io] */ + +precision mediump float; +in vec2 vertex_position; +in vec2 vertex_uv; +in vec3 vertex_color; +out vec2 uv; +out vec3 color; +uniform mat4 model_transformation; +uniform mat4 orthographic_projection; + +void main(void) +{ + gl_Position = orthographic_projection * model_transformation * vec4(vertex_position, 0, 1); + uv = vertex_uv; + color = vertex_color; +}