initial commit

This commit is contained in:
frank 2022-06-16 15:24:14 -04:00
commit 4bf9b6cf9d
29 changed files with 1186 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
index.*
local/
*.o
BPmono.ttf
pepy
compile_commands.json

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "lib/sb"]
path = lib/sb
url = makar:/var/www/git/sb

192
Makefile Normal file
View File

@ -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

38
config.json Normal file
View File

@ -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
}
}

1
lib/sb Submodule

@ -0,0 +1 @@
Subproject commit ee119ecc7e4287838cec5aac127820d4738de17f

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
resource/space.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
resource/wad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

180
shell.html Normal file
View File

@ -0,0 +1,180 @@
<!doctype html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>B.U.D.D.I.</title>
<style>
@import url("../../lib/fonts/notcouriersans/regular/style.css");
body {margin: 0; padding: 0}
.emscripten { display: block; }
textarea.emscripten { font-family: NotCourierSans; font-size: 24px; width: 960px; resize: none; border: 0px; overflow:hidden }
div.emscripten_border { border: 0px solid black; }
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
canvas.emscripten
{
border: 0px none;
background-color: black;
width: 960px;
height: 540px;
}
.spinner {
margin: auto;
height: 50px;
width: 50px;
-webkit-animation: rotation .8s linear infinite;
-moz-animation: rotation .8s linear infinite;
-o-animation: rotation .8s linear infinite;
animation: rotation 0.8s linear infinite;
border-left: 10px solid rgb(0,150,240);
border-right: 10px solid rgb(0,150,240);
border-bottom: 10px solid rgb(0,150,240);
border-top: 10px solid rgb(100,0,200);
border-radius: 100%;
background-color: rgb(200,100,250);
}
div#loading
{
text-align: center;
position: absolute;
top: 90px;
width: 960px;
color: #fff;
font-size: 22px;
}
@-webkit-keyframes rotation {
from {-webkit-transform: rotate(0deg);}
to {-webkit-transform: rotate(360deg);}
}
@-moz-keyframes rotation {
from {-moz-transform: rotate(0deg);}
to {-moz-transform: rotate(360deg);}
}
@-o-keyframes rotation {
from {-o-transform: rotate(0deg);}
to {-o-transform: rotate(360deg);}
}
@keyframes rotation {
from {transform: rotate(0deg);}
to {transform: rotate(360deg);}
}
div#game
{
position: relative;
width: 960px;
}
</style>
</head>
<body>
<div>
<image style="width: 140px" src="resource/wad/Mike/0.png">Pepy, it's cuckoo time! Please make it as big as possible using the arrow keys in
10 seconds.
</div>
<div id="game">
<div id="loading">
<figure style="overflow:visible;" id="spinner"><div class="spinner"></div></figure>
<div class="emscripten" id="status">Downloading...</div>
<div class="emscripten">
<progress value="0" max="100" id="progress" hidden=1></progress>
</div>
</div>
<div class="emscripten_border">
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
</div>
</div>
<textarea class="emscripten" id="output" rows="8" readonly spellcheck="false" wrap="off"></textarea>
<img src="resource/Book_of_Pepy/walking_pepy_london.png" /><br />
<img src="resource/Book_of_Pepy/1071003012.jpg" /><br />
<img src="resource/Book_of_Pepy/129548486_1656033331242674_6748215393958512364_n.jpg" /><br />
<img src="resource/Book_of_Pepy/1gfxra.jpg" /><br />
<img src="resource/Book_of_Pepy/b89d70e6fb9c4caaed2e797f01bb7575.jpg" /><br />
<img src="resource/Book_of_Pepy/cat-party-hat-eats-fish-burger-beige-multi-colored-eating-big-fresh-white-square-plate-drinking-beer-table-183867588.jpg" /><br />
<img src="resource/Book_of_Pepy/E6IhBxRXIAETJ29.jpeg" /><br />
<img src="resource/Book_of_Pepy/E9GrqOlWYAE2Sgw.jpeg" /><br />
<img src="resource/Book_of_Pepy/Ea1zDCEUwAAKUcF.jpeg" /><br />
<img src="resource/Book_of_Pepy/EcrFXg5VcAE6tK6.jpeg" /><br />
<img src="resource/Book_of_Pepy/Japanese-chocolate-whisky-Morinaga-twig-big-Koeda-whisky-pairing-buy-special-drink-review-photos-4.webp" /><br />
<img src="resource/Book_of_Pepy/large.webp" /><br />
<img src="resource/Book_of_Pepy/Lenny-Face.png" /><br />
<img src="resource/Book_of_Pepy/necropolis_of_Pepy.png" /><br />
<img src="resource/Book_of_Pepy/vz-ff8724ba-584e-4f9d-bae5-f7c15e462512.jpeg" /><br />
<script type='text/javascript'>
var statusElement = document.getElementById('status');
var progressElement = document.getElementById('progress');
var spinnerElement = document.getElementById('spinner');
var Module = {
preRun: [],
postRun: [],
print: (function() {
var element = document.getElementById('output');
if (element) element.value = ''; // clear browser cache
return function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
// These replacements are necessary if you render to raw HTML
//text = text.replace(/&/g, "&amp;");
//text = text.replace(/</g, "&lt;");
//text = text.replace(/>/g, "&gt;");
//text = text.replace('\n', '<br>', 'g');
console.log(text);
if (element) {
element.value += text + "\n";
element.scrollTop = element.scrollHeight; // focus on bottom
}
};
})(),
printErr: function(text) {
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
},
canvas: (function() {
var canvas = document.getElementById('canvas');
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
// application robust, you may want to override this behavior before shipping!
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
canvas.addEventListener("webglcontextlost", function(e) {
alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
return canvas;
})(),
setStatus: function(text) {
if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
if (text === Module.setStatus.last.text) return;
var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
var now = Date.now();
if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
Module.setStatus.last.time = now;
Module.setStatus.last.text = text;
if (m) {
text = m[1];
progressElement.value = parseInt(m[2])*100;
progressElement.max = parseInt(m[4])*100;
progressElement.hidden = false;
spinnerElement.hidden = false;
} else {
progressElement.value = null;
progressElement.max = null;
progressElement.hidden = true;
if (!text) spinnerElement.hidden = true;
}
statusElement.innerHTML = text;
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
}
};
Module.setStatus('Downloading...');
window.onerror = function() {
Module.setStatus('Exception thrown, see JavaScript console');
spinnerElement.style.display = 'none';
Module.setStatus = function(text) {
if (text) Module.printErr('[post-exception status] ' + text);
};
};
</script>
{{{ SCRIPT }}}
</body>
</html>

160
src/Model.cpp Normal file
View File

@ -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<std::string, std::shared_ptr<sb::Attributes>>& 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<std::string, sb::Attributes>& 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<std::string>& 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<std::string, std::shared_ptr<sb::Attributes>>& 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<sb::Attributes>& 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<sb::Attributes>& 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<sb::Attributes>(attributes), name);
}
/* Assign name to attributes and share ownership. */
void Model::attributes(const std::shared_ptr<sb::Attributes>& 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<std::string, sb::Texture>& 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;
}

75
src/Model.hpp Normal file
View File

@ -0,0 +1,75 @@
/* Cuckoo by @ohsqueezy [ohsqueezy.itch.io] */
#ifndef MODEL_H_
#define MODEL_H_
/* GL functions */
#if defined(__EMSCRIPTEN__)
#include <GL/glew.h>
#else
#include "glew/glew.h"
#endif
#include <iostream>
#include <string>
#include <map>
#include <memory>
#include <iterator>
#include "glm/glm.hpp"
#include "Attributes.hpp"
#include "Texture.hpp"
class Model
{
private:
inline static const std::string DEFAULT_TEXTURE_NAME = "default";
std::map<std::string, sb::Texture> model_textures;
std::map<std::string, std::shared_ptr<sb::Attributes>> model_attributes;
glm::mat4 model_transformation {1.0f};
public:
Model();
Model(const std::map<std::string, std::shared_ptr<sb::Attributes>>&);
Model(const std::map<std::string, sb::Attributes>&);
Model(const std::initializer_list<std::string>&);
std::map<std::string, std::shared_ptr<sb::Attributes>>& attributes();
std::shared_ptr<sb::Attributes>& attributes(const std::string&);
void attributes(const sb::Attributes&, const std::string&);
void attributes(const std::shared_ptr<sb::Attributes>&, const std::string&);
std::shared_ptr<sb::Attributes>& operator[](const std::string&);
void enable();
void disable();
std::map<std::string, sb::Texture>& 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<sb::Attributes> position = std::make_shared<sb::Attributes>(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<sb::Attributes> uv = std::make_shared<sb::Attributes>(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<std::string, std::shared_ptr<sb::Attributes>>({{"position", position}, {"uv", uv}})) {}
};
#endif

383
src/Pepy.cpp Normal file
View File

@ -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<int>() + 1;
std::vector<glm::vec2> outer_points = sb::points_on_circle(point_count, 0.75f, {0.0f, 0.0f});
std::vector<glm::vec2> 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<float>(point_count) * 255.0f, outer_saturation, outer_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast<float>(point_count) * 255.0f, outer_saturation, outer_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast<float>(point_count) * 255.0f, inner_saturation, inner_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast<float>(point_count) * 255.0f, inner_saturation, inner_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast<float>(point_count) * 255.0f, inner_saturation, inner_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast<float>(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<float>(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f,
(1.0f - static_cast<float>(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<float>() * 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<float>();
}
}
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<glm::vec2> 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<const GLfloat*>(&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<Plane, glm::vec2>& 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<const GLfloat*>(&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<const GLfloat*>(&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;
}
}

85
src/Pepy.hpp Normal file
View File

@ -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 <glm/glm.hpp>
#include <glm/gtx/compatibility.hpp>
/* [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<std::pair<Plane, glm::vec2>> wad;
std::map<std::string, GLuint> 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

44
src/shader.frag Normal file
View File

@ -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));
}

19
src/shader.vert Normal file
View File

@ -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;
}