From 70d1d18615123d578aded0c882cbf24e4c7d8c8a Mon Sep 17 00:00:00 2001 From: frank Date: Tue, 23 May 2023 15:35:29 -0400 Subject: [PATCH] ic --- .gitignore | 12 ++ .gitmodules | 3 + Makefile | 188 +++++++++++++++++++++++ config.json | 42 ++++++ config_android.json | 16 ++ lib/sb | 1 + src/Cakefoot.cpp | 183 +++++++++++++++++++++++ src/Cakefoot.hpp | 319 ++++++++++++++++++++++++++++++++++++++++ src/shaders/flat.frag | 44 ++++++ src/shaders/flat.vert | 24 +++ src/shaders/mvp.frag | 76 ++++++++++ src/shaders/mvp.vert | 67 +++++++++ src/shaders/shader.frag | 78 ++++++++++ src/shaders/shader.vert | 67 +++++++++ 14 files changed, 1120 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 config.json create mode 100644 config_android.json create mode 160000 lib/sb create mode 100644 src/Cakefoot.cpp create mode 100644 src/Cakefoot.hpp create mode 100644 src/shaders/flat.frag create mode 100644 src/shaders/flat.vert create mode 100644 src/shaders/mvp.frag create mode 100644 src/shaders/mvp.vert create mode 100644 src/shaders/shader.frag create mode 100644 src/shaders/shader.vert diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0fb8fd4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Compilation +*.o +*.data +*.wasm +build/ +Cakefoot.x86_64 + +# Auto complete +compile_commands.json + +# SPACEBOX +BPmono.ttf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fbb5703 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/sb"] + path = lib/sb + url = https://open.shampoo.ooo/shampoo/spacebox diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..586ff84 --- /dev/null +++ b/Makefile @@ -0,0 +1,188 @@ +# >> Cakefoot << +# +# This Makefile is used to create a Linux, web, or Android build. +# +# The [SPACEBOX] game framework source code from is required. + +######### +# Paths # +######### + +# Location of project specific source files +SRC_DIR := src/ + +# Locations of [SPACEBOX] source and its packaged dependencies +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 +CXX := clang++ + +# Location of SDL config program. See README.md for how to compile the SDL library and this utility. +SDLCONFIG := $(HOME)/local/sdl/bin/sdl2-config + +# Include BPmono.ttf in the project +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 Connection.o Switch.o Carousel.o time_it.o),$(SB_H_FILES:.hpp=.o)) +SRC_H_FILES := $(wildcard $(addprefix $(SRC_DIR),*.hpp)) +SRC_O_FILES := $(SRC_H_FILES:.hpp=.o) + +################################################################## +# Object files for [SPACEBOX], its dependencies, and the project # +################################################################## + +$(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) +$(SB_SRC_DIR)Model.o : $(addprefix $(SB_SRC_DIR),extension.hpp Attributes.hpp Texture.hpp utility.hpp Carousel.hpp) +$(SRC_DIR)Cakefoot.o : $(SRC_H_FILES) $(SB_H_FILES) +%.o : %.cpp %.hpp + $(CXX) $(CXXFLAGS) $< -c -o $@ + +############### +# Linux build # +############### + +Cakefoot.x86_64 : CFLAGS = -g -Wall -Wextra -O1 -c -I$(SB_LIB_DIR) -I$(SB_SRC_DIR) $(SDL_CFLAGS) \ + -I $(HOME)/ext/software/emsdk/upstream/emscripten/system/include +Cakefoot.x86_64 : CXXFLAGS = $(CFLAGS) --std=c++17 +Cakefoot.x86_64 : LFLAGS = $(SDL_LFLAGS) -Wl,--enable-new-dtags -lpthread -lGL -lGLESv2 -lSDL2_image -lSDL2_ttf -lSDL2_mixer -lstdc++fs +Cakefoot.x86_64 : $(GLEW_DIR)glew.o $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) $(SB_O_FILES) $(SRC_O_FILES) + $(CREATE_FONT_SYMLINK) + $(CXX) $^ $(LFLAGS) -D__LINUX__ -o Cakefoot.x86_64 + +############# +# Web build # +############# + +# Use Emscripten to output JavaScript + +EMSCRIPTENHOME = $(HOME)/ext/software/emsdk/upstream/emscripten +EMSCRIPTEN_CFLAGS = -O0 -Wall -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s SDL2_IMAGE_FORMATS="['png', 'jpg']" -s USE_SDL_TTF=2 -s USE_SDL_MIXER=2 \ + --no-heap-copy -I $(SB_LIB_DIR) -I $(SB_SRC_DIR) +EMSCRIPTEN_LFLAGS = -s MIN_WEBGL_VERSION=2 -s EXPORTED_FUNCTIONS="['_main', '_malloc']" -s ALLOW_MEMORY_GROWTH=1 -s FULL_ES3=1 \ + -sLLD_REPORT_UNDEFINED -sNO_DISABLE_EXCEPTION_CATCHING +EMSCRIPTEN_PRELOADS = --preload-file "BPmono.ttf"@/ --preload-file "config.json"@/ --preload-file "resource/"@/"resource/" \ + --preload-file "src/shaders/"@/"src/shaders/" + +cakefoot.js : CC = $(EMSCRIPTENHOME)/emcc +cakefoot.js : CXX = $(EMSCRIPTENHOME)/em++ +cakefoot.js : CFLAGS = $(EMSCRIPTEN_CFLAGS) +cakefoot.js : CXXFLAGS = $(CFLAGS) --std=c++17 +cakefoot.js : $(addprefix $(SDLGFX2_DIR),SDL2_rotozoom.o SDL2_gfxPrimitives.o) $(SB_O_FILES) $(SRC_O_FILES) + $(CREATE_FONT_SYMLINK) + $(CXX) $^ $(CXXFLAGS) $(EMSCRIPTEN_LFLAGS) $(EMSCRIPTEN_PRELOADS) -o cakefoot.js + +################# +# Android build # +################# + +# Detailed info on how this build target was originally created for the fill_screen demo is in README.md at the root of the repository. It +# requires the Android SDK and the source packages for SDL. The paths below should be edited to match the local system. Icon creation requires +# Imagemagick's convert tool from . + +SDL_SRC := $(HOME)/ext/software/SDL2-2.26.3 +SDL_IMAGE_SRC := $(HOME)/ext/software/SDL2_image-2.6.2-android +SDL_MIXER_SRC := $(HOME)/ext/software/SDL2_mixer-2.6.2-android +SDL_TTF_SRC := $(HOME)/ext/software/SDL2_ttf-2.20.1-android +SDL_ANDROID_PROJECT := $(SDL_SRC)/android-project +ANDROID_MK := app/jni/src/Android.mk +ANDROID_APP_MK := app/jni/Application.mk +ANDROID_MANIFEST := app/src/main/AndroidManifest.xml +ANDROID_SDK := $(HOME)/local/Android +ANDROID_PACKAGE := ooo.shampoo.foam +ANDROID_BUILD_DIR := build/android/$(ANDROID_PACKAGE) +ANDROID_CLASS := Cakefoot +ANDROID_CLASS_DIR := app/src/main/java/$(subst .,/,$(ANDROID_PACKAGE)) + +# The skeleton for the Android build is based on the SDL android-project. The libraries are symlinked in. If the script that rewrites the skeleton +# has changed, start with a fresh skeleton. Use the SPACEBOX revise skeleton script to edit the SDL android-project parameters. +$(ANDROID_BUILD_DIR): $(SDL_SRC)/android-project/ $(SB_SRC_DIR)/android/revise_skeleton.sh + -mkdir -p $(ANDROID_BUILD_DIR) + rsync -ar $(SDL_SRC)/android-project/ $(ANDROID_BUILD_DIR) + ln -nsf $(SDL_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL + ln -nsf $(SDL_IMAGE_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL2_image + ln -nsf $(SDL_MIXER_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL2_mixer + ln -nsf $(SDL_TTF_SRC) $(ANDROID_BUILD_DIR)/app/jni/SDL2_ttf + $(SB_SRC_DIR)/android/revise_skeleton.sh $(ANDROID_PACKAGE) $(ANDROID_BUILD_DIR) $(ANDROID_MANIFEST) $(ANDROID_APP_MK) $(ANDROID_MK) $(ANDROID_CLASS) \ + $(ANDROID_APP_NAME) $(ANDROID_MIN_TARGET) $(ANDROID_NDK) "Cakefoot" "21" "24.0.8215888" $(SB_SRC_DIR) $(SB_LIB_DIR) $(SRC_DIR) + ln -nsf $(OPENCV_ANDROID_SDK)/sdk/native/libs $(ANDROID_BUILD_DIR)/app/jni/src/opencv + ln -nsf $(CURL_ANDROID)/jni/build/curl $(ANDROID_BUILD_DIR)/app/jni/src/curl + ln -nsf $(CURL_ANDROID)/jni/build/openssl $(ANDROID_BUILD_DIR)/app/jni/src/openssl + sed -i "s/^APP_CPPFLAGS.*/& -nostartfiles/" "$(ANDROID_BUILD_DIR)/$(ANDROID_APP_MK)" + sed -i "s/org.libsdl.app/$(ANDROID_PACKAGE)/" "$(ANDROID_BUILD_DIR)/app/build.gradle" "$(ANDROID_BUILD_DIR)/$(ANDROID_MANIFEST)" + +# Extend the SDLActivity class +$(ANDROID_BUILD_DIR)/$(ANDROID_CLASS_DIR)/$(ANDROID_CLASS).java: $(SB_SRC_DIR)/android/main_class.sh + $(SB_SRC_DIR)/android/main_class.sh $(ANDROID_PACKAGE) $(ANDROID_CLASS) $(ANDROID_BUILD_DIR)/$(ANDROID_CLASS_DIR) + +# Generate icon +$(ANDROID_BUILD_DIR)/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: $(SB_SRC_DIR)/android/generate_icon.sh $(SB_DIR)/icon + $(SB_SRC_DIR)/android/generate_icon.sh $(ANDROID_BUILD_DIR) "$(SB_DIR)/icon/foreground.png" "$(SB_DIR)/icon/background.png" + +# Custom assets +$(ANDROID_BUILD_DIR)/app/src/main/assets: config*.json $(shell find resource/) $(shell find src/shaders) + -mkdir -p $(ANDROID_BUILD_DIR)/app/src/main/assets + rsync -ar --relative config*.json resource src/shaders $(ANDROID_BUILD_DIR)/app/src/main/assets + +# Run gradle and generate an APK +$(ANDROID_BUILD_DIR)/app-debug.apk: $(ANDROID_BUILD_DIR) $(ANDROID_BUILD_DIR)/$(ANDROID_CLASS_DIR)/$(ANDROID_CLASS).java \ + $(ANDROID_BUILD_DIR)/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml $(ANDROID_BUILD_DIR)/app/src/main/assets + ANDROID_SDK_ROOT=$(ANDROID_SDK) $(ANDROID_BUILD_DIR)/gradlew -p $(ANDROID_BUILD_DIR) build + ln -nsf app/build/outputs/apk/debug/app-debug.apk $(ANDROID_BUILD_DIR) + ln -nsf app/build/outputs/apk/debug/app-release-unsigned.apk $(ANDROID_BUILD_DIR) + +######################### +# Clean up object files # +######################### + +clean : + -find $(SRC_DIR) -iname "*.o" -delete + +clean-all : clean + -find $(SB_SRC_DIR) -iname "*.o" -delete + -find $(SB_LIB_DIR) -iname "*.o" -delete + +############# +# 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)/ext/software/compiledb/bin/compiledb +compiledb : + -$(PATH_TO_COMPILEDB) -n make Cakefoot.x86_64 -k diff --git a/config.json b/config.json new file mode 100644 index 0000000..a3bb487 --- /dev/null +++ b/config.json @@ -0,0 +1,42 @@ +{ + "display": + { + "dimensions": [864, 486], + "framerate": 60, + "title": "Cakefoot", + "debug": false, + "render driver": "opengl", + "show-cursor": true + }, + + "configuration": + { + "auto-refresh": true + }, + + "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" + }, + + "input": + { + "suppress-any-key-on-mods": true + }, + + "log": + { + "enabled": true, + "output-directory": "/var/log/sb/", + "debug-to-stdout": true, + "debug-to-file": true, + "info-file-name": "cakefoot_info.log", + "debug-file-name": "cakefoot_debug.log" + } +} diff --git a/config_android.json b/config_android.json new file mode 100644 index 0000000..fd36a2c --- /dev/null +++ b/config_android.json @@ -0,0 +1,16 @@ +{ + "display": + { + "render driver": "opengles2" + }, + + "recording": + { + "enabled": false + }, + + "log": + { + "enabled": false + } +} diff --git a/lib/sb b/lib/sb new file mode 160000 index 0000000..3ba3be4 --- /dev/null +++ b/lib/sb @@ -0,0 +1 @@ +Subproject commit 3ba3be449618ef2a7a5a46cef61492d57d04ed82 diff --git a/src/Cakefoot.cpp b/src/Cakefoot.cpp new file mode 100644 index 0000000..72585a9 --- /dev/null +++ b/src/Cakefoot.cpp @@ -0,0 +1,183 @@ +/* >> Cakefoot << */ + +#if defined(__ANDROID__) || defined(ANDROID) +#include +#endif + +#include "Cakefoot.hpp" + +Cakefoot::Cakefoot() +{ +#ifdef __ANDROID__ + SDL_SetHint(SDL_HINT_ORIENTATIONS, "Portrait"); +#endif + + start_button.scale(0.5f); + start_button.on_state_change([&](bool state, int count){ + if (state) + { + std::ostringstream message; + message << "Hello, " << state << " World! " << count; + sb::Log::log(message); + start_button.press(1); + } + }); + + /* subscribe to command events */ + delegate().subscribe(&Cakefoot::respond, this); + delegate().subscribe(&Cakefoot::respond, this, SDL_MOUSEMOTION); + delegate().subscribe(&Cakefoot::respond, this, SDL_MOUSEBUTTONDOWN); + + /* loading GL context for 3D */ + load_gl_context(); + + /* Load a pointer cursor from the system library that will be freed automatically */ + poke = std::shared_ptr(SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND), SDL_FreeCursor); +} + +void Cakefoot::load_gl_context() +{ + super::load_gl_context(); + + /* Generate a vertex array object ID, bind it as current (requirement of OpenGL) */ + vao.generate(); + vao.bind(); + + /* Generate ID for the vertex buffer object that will hold all vertex data. Using one buffer for all attributes, data + * will be copied in one after the other. */ + vbo.generate(); + vbo.bind(); + + /* Load shader program */ + GLuint vertex_shader = load_shader("src/shaders/shader.vert", GL_VERTEX_SHADER); + GLuint fragment_shader = load_shader("src/shaders/shader.frag", GL_FRAGMENT_SHADER); + shader_program = glCreateProgram(); + glAttachShader(shader_program, vertex_shader); + glAttachShader(shader_program, fragment_shader); + sb::Plane::position->bind(0, shader_program, "vertex_position"); + sb::Plane::uv->bind(1, shader_program, "vertex_uv"); + sb::Plane::color->bind(2, shader_program, "vertex_color"); + sb::Log::gl_errors("after loading shaders"); + + /* Fill VBO with attribute data */ + vbo.allocate(start_button.size(), GL_STATIC_DRAW); + vbo.add(*sb::Plane::position); + vbo.add(*sb::Plane::uv); + vbo.add(*sb::Plane::color); + sb::Log::gl_errors("after filling VBO"); + + /* link shaders */ + link_shader(shader_program); + glUseProgram(shader_program); + sb::Log::gl_errors("after linking"); + + /* store uniform locations after linking */ + uniform["mvp"] = glGetUniformLocation(shader_program, "mvp"); + uniform["time"] = glGetUniformLocation(shader_program, "time"); + uniform["effect"] = glGetUniformLocation(shader_program, "effect"); + uniform["uv transformation"] = glGetUniformLocation(shader_program, "uv_transformation"); + uniform["coordinate bound"] = glGetUniformLocation(shader_program, "coordinate_bound"); + uniform["model texture"] = glGetUniformLocation(shader_program, "model_texture"); + uniform["texture enabled"] = glGetUniformLocation(shader_program, "texture_enabled"); + + /* enable alpha rendering */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + sb::Log::gl_errors("after uniform locations"); +} + +void Cakefoot::respond(SDL_Event& event) +{ + /* Mouse interface */ + if (event.type == SDL_MOUSEMOTION || event.type == SDL_MOUSEBUTTONDOWN) + { + /* Get mouse coordinates in pixel resolution and NDC */ + glm::vec2 mouse_pixel = event.type == SDL_MOUSEBUTTONDOWN ? glm::vec2{event.button.x, event.button.y} : + glm::vec2{event.motion.x, event.motion.y}; + glm::vec2 mouse_ndc { + float(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f, (1.0f - float(mouse_pixel.y) / window_box().height()) * 2.0f - 1.0f + }; + + /* Collide with start button */ + bool over_start_button = start_button.collide(mouse_ndc); + + /* Check for press */ + if (over_start_button) + { + /* Set cursor to pokey finger */ + if (SDL_GetCursor() != poke.get()) + { + SDL_SetCursor(poke.get()); + } + + /* Respond to a click */ + if (event.type == SDL_MOUSEBUTTONDOWN) + { + /* Reset cursor to default arrow */ + // SDL_SetCursor(SDL_GetDefaultCursor()); + start_button.press(0); + } + } + else if (SDL_GetCursor() == poke.get()) + { + SDL_SetCursor(SDL_GetDefaultCursor()); + } + } + else if (sb::Delegate::compare(event, "reset")) + { + sb::Log::log("Goodbye, World!"); + } +} + +void Cakefoot::destroy_texture(GLuint* texture_id) +{ + /* not sure why SDL_Log works here but SDL_LogDebug and SDL_LogInfo don't */ + std::ostringstream message; + message << "destroying texture ID " << *texture_id; + sb::Log::log(message); + glDeleteTextures(1, texture_id); +} + +void Cakefoot::update() +{ + sb::Log::gl_errors("at beginning of update"); + /* Time in seconds the game has running for */ + float time_seconds = SDL_GetTicks() / 1000.0f; + + glDisable(GL_DEPTH_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + /* flat shader uniforms: time, texture ID, disabled HSV blend, scroll on */ + glActiveTexture(GL_TEXTURE0); + glUniform1f(uniform["time"], time_seconds); + glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &glm::mat4(1)[0][0]); + + /* calculate the transformation matrix for displaying pudding in viewport */ + model = glm::rotate(model, 0.0f, Y_UNIT_NORMAL_3D); + projection = glm::perspective(glm::radians(40.0f * 1 / window_box().aspect()), window_box().aspect(), 0.1f, 100.0f); + mvp = projection * VIEW_MATRIX * model; + + /* uniforms */ + // glUniformMatrix4fv(uniform["mvp"], 1, GL_FALSE, &mvp[0][0]); + + /* draw pudding model */ + // glEnable(GL_DEPTH_TEST); + + /* draws bg vertices and texture */ + // cake_model.enable(); + start_button.draw(uniform["mvp"], uniform["texture enabled"]); + // glDrawArrays(GL_TRIANGLES, 0, cake_model.attributes("position")->count()); + + /* Display */ + SDL_GL_SwapWindow(window()); + sb::Log::gl_errors("at end of update"); +} + +int main() +{ + Cakefoot game = Cakefoot(); + game.run(); + game.quit(); + return 0; +} diff --git a/src/Cakefoot.hpp b/src/Cakefoot.hpp new file mode 100644 index 0000000..6265ec9 --- /dev/null +++ b/src/Cakefoot.hpp @@ -0,0 +1,319 @@ +/* >> Cakefoot << */ + +#pragma once + +/* Needed for functions in glm/gtx/ */ +#define GLM_ENABLE_EXPERIMENTAL + +/* Standard library includes */ +#include +#include +#include +#include +// #include +// #include +// #include +#include +// #include +#include +// #include + +/* Include Game.hpp before any other SDL-related headers because it defines SDL_MAIN_HANDLED */ +#include "Game.hpp" + +/* SPACEBOX external libraries included in source package */ +#include "sdl2-gfx/SDL2_gfxPrimitives.h" +#include "json/json.hpp" +#include "glm/glm.hpp" +#include "glm/gtx/matrix_decompose.hpp" +#include "glm/gtc/matrix_access.hpp" + +/* SPACEBOX classes and functions */ +#include "Color.hpp" +#include "extension.hpp" +#include "filesystem.hpp" +#include "Animation.hpp" +#include "Texture.hpp" +#include "GLObject.hpp" +#include "Log.hpp" +#include "Attributes.hpp" +#include "VBO.hpp" +#include "Model.hpp" +#include "utility.hpp" +#include "Box.hpp" +#include "Switch.hpp" + +/*! + * A Pad is a Plane which can be clicked to launch an arbitrary user function. It can be sized and placed by setting its + * translation and scale values. + * + * Each instance: + * + * - Shares vertices and UV in VBO + * - Has its own Texture representing the button on-screen + * - Has its own response to click + * - Shares mouse collision code + * - Has its own translate + scale transformation + * + * Example: + * + * glm::vec3 w = glm::mat3({{1, 0, 0}, {0, 1, 0}, {-0.6739, -0.74, 1}}) * glm::mat3({{.1, 0, 0}, {0, .1 * (460.0 / 768.0), 0}, {0, 0, 1}}) * + * glm::vec3({-1, -1, 1}); + * std::cout << w << std::endl << glm::translate(glm::vec3{-0.6739, -0.74, 0}) * + * glm::scale(glm::vec3{.1, .1 * (460.0 / 768.0), 1}) * glm::vec4{-1, -1, 0, 1} << std::endl; + * Pad p {background.current(), {-0.6739f, -0.74f}, 0.1f, get_display().window_box().aspect(), std::function()}; + * const std::vector& p_position = *p.attributes("position"); + * glm::vec4 final_position = p.transformation() * glm::vec4{p_position[2].x, p_position[2].y, 0, 1}; + * std::cout << p.transformation() << std::endl << final_position << std::endl; + * assert(final_position == glm::vec4({w.x, w.y, 0, 1})); + */ +template +class Pad : public sb::Plane +{ + +private: + + inline static const glm::vec3 ROTATION_AXIS {0.0f, 0.0f, 1.0f}; + using Reaction = std::function; + sb::Switch connection; + Box collision_box; + float rotation_angle = 0.0f, scale_factor = 1.0f, scale_ratio = 1.0f; + glm::vec2 translation_vector {0.0f, 0.0f}; + + + /*! + * Set the transformation matrix for the pad object by applying the scale to the translation and the rotation to the + * resulting matrix, meaning the transformations will be applied to the pad in the order of: translate, scale, and + * rotate. The collision box will be scaled and moved to fit around the position coordinates that would result from + * applying this transformation to the position coordinates. + */ + void transform() + { + glm::vec3 scale { scale_factor, scale_factor, 1.0f }; + if (scale_ratio > 1.0f) + { + scale.x /= scale_ratio; + } + else if (scale_ratio < 1.0f) + { + scale.y *= scale_ratio; + } + collision_box.size(2.0f * glm::vec2{scale.x, scale.y}, true); + collision_box.center(translation_vector); + sb::Model::transformation(glm::translate(glm::vec3{translation_vector.x, translation_vector.y, 0.0f}) * + glm::scale(scale) * glm::rotate(rotation_angle, ROTATION_AXIS)); + } + +public: + + /*! + * Construct a Pad object without a texture. + * + * @overload Pad(sb::Texture, glm::vec2, flat, float, Reaction, float) + */ + Pad(glm::vec2 translation = {0.0f, 0.0f}, float scale = 1.0f, float ratio = 1.0f, Reaction on_state_change = Reaction(), float rotation = 0.0f) + { + this->translation(translation); + this->scale(scale, ratio); + if (rotation) + { + this->rotation(rotation); + } + this->on_state_change(on_state_change); + collision_box.invert_y(true); + } + + /*! + * Construct a Pad from a texture, a translation amount, a scale factor, and a reaction function. The translation is relative + * to (0.0, 0.0), and the scale is relative to the superclass Plane object, which has opposite corners at (-1.0, -1.0) and + * (1.0, 1.0). The texture is the graphic that displays in the Pad location. The reaction must accept a boolean as its first + * argument, which will be the state of the contained Switch object. + * + * @param texture pad display graphic + * @param translation x, y amount to translate the pad position + * @param scale amount to scale both x and y + * @param ratio ratio to adjust scale of x and y + * @param on_state_change reaction function which accepts a boolean as its first argument + * @param rotation angle in radians to rotate the pad + */ + Pad(sb::Texture texture, glm::vec2 translation = {0.0f, 0.0f}, float scale = 1.0f, float ratio = 1.0f, + Reaction on_state_change = Reaction(), float rotation = 0.0f) : Pad(translation, scale, ratio, on_state_change, rotation) + { + this->texture(texture); + } + + /*! + * Set angle in radians the pad will be rotated. The pad will be rotated around its center. The collision box will not + * change, so the box will not contain the entire pad if the angle is not a multiple of pi/2. The pad's transformation + * matrix will automatically be set to incorporate this rotation transformation. + * + * @param angle angle in radians to rotate pad + */ + void rotation(float angle) + { + rotation_angle = angle; + transform(); + } + + /*! + * Set the scale using a factor and ratio that will transform the pad in the X and Y dimensions. The ratio will determine + * how much each axis is scaled. If the ratio is above one, the X-axis's scale will be divided by the ratio. If the ratio + * is below one, the Y-axis's scale will be multiplied by the aspect ratio. If the aspect ratio of the window is given, + * this will force the pad to display as a square, and the ratio will be relative to the shorter axis. The collision box + * will be scaled by the same factors. The pad's transformation matrix will automatically be set to incorporate this + * scale transformation. + * + * @param factor amount to scale in both x and y directions + * @param ratio amount to adjust scaling, set to the window aspect ratio to make the pad appear square + */ + void scale(float factor, float ratio = 1.0f) + { + scale_factor = factor; + scale_ratio = ratio; + transform(); + } + + /*! + * Set a translation for the pad object in the X and Y dimension using a 2d vector. The collision box will be moved by the + * same translation. The pad's transformation matrix will automatically be set to incorporate this translation + * transformation. + * + * @param translation x, y distance to translate the pad + */ + void translation(const glm::vec2& translation) + { + translation_vector = translation; + transform(); + } + + /*! + * Set the function that will run when a pad object is clicked. + * + * @param on_state_change reaction function which accepts a boolean as its first argument + */ + void on_state_change(Reaction reaction) + { + connection.on_state_change(reaction); + } + + /*! + * Returns true if the point at given position collides with the pad's collision box. + * + * @param position x, y coordinate to check for collision with Pad::collision_box + * @return true if the point is inside Pad::collision_box, false otherwise + */ + bool collide(const glm::vec2& position) const + { + return collision_box.collide(position); + } + + /*! + * Set transformation uniform, bind texture, and draw vertices. + * + * @param uniform_id transformation uniform ID + * @param texture_flag_uniform_id uniform ID for boolean enabling or disabling texture display + */ + void draw(GLuint uniform_id, GLuint texture_flag_uniform_id) + { + glUniformMatrix4fv(uniform_id, 1, GL_FALSE, &transformation()[0][0]); + if (!textures().empty()) + { + glUniform1i(texture_flag_uniform_id, true); + texture().bind(); + } + else + { + glUniform1i(texture_flag_uniform_id, false); + } + enable(); + glDrawArrays(GL_TRIANGLES, 0, attributes("position")->count()); + disable(); + } + + return_type press(arguments... args) + { + return connection.flip(args...); + } +}; + +/*! + * The main game object. There is currently only support for one of these to exist at a time. + */ +class Cakefoot : public Game +{ + +private: + + /* Defines for effect IDs that will be passed to the shader program. Since EFFECT_COUNT is last and every value + * is the default integer, it will be set to the number of effects available. */ + enum Effect + { + EFFECT_NONE, + EFFECT_COUNT + }; + + /* Defines for UV transformations available in the fragment shader program */ + enum UVTransformation + { + UV_NONE, + UV_SQUIRCLE + }; + + /* Convention for calling parent class in a consistent way across classes */ + typedef Game super; + + /* Constants */ + inline static const glm::vec3 ZERO_VECTOR_3D {0, 0, 0}; + inline static const glm::vec3 Y_UNIT_NORMAL_3D {0, 1, 0}; + inline static const glm::mat4 VIEW_MATRIX = glm::lookAt({4.0f, 2.0f, 1.0f}, {0.0f, -0.325f, 0.0f}, Y_UNIT_NORMAL_3D); + + /* Member variables */ + std::shared_ptr poke; + int effect_id = EFFECT_NONE; + std::map uniform; + GLuint shader_program; + glm::mat4 projection, model {1.0f}, mvp; + sb::VAO vao; + sb::VBO vbo; + Pad start_button; + sb::Plane cake_model; + + /*! + * Create GL context via super class and load vertices, UV data, and shaders. + */ + void load_gl_context(); + + /*! + * Call GL's delete texture function, and print a debug statement for testing. This is defined as a static member + * function and uses the SDL logging function instead of the inherited logging functions from Node since the object + * may not be allocated at destruction time (?) + * + * @param texture_id GL texture ID being destroyed + */ + static void destroy_texture(GLuint* texture_id); + +public: + + /*! + * Initialize a Cakefoot instance + */ + Cakefoot(); + + /*! + * Respond to command events + */ + void respond(SDL_Event&); + + /*! + * Update parameters and draw the screen + */ + void update(); + +}; + +/*! + * Create a Cakewalk instance and launch its mainloop. + * + * @return Always returns 0 + */ +int main(); diff --git a/src/shaders/flat.frag b/src/shaders/flat.frag new file mode 100644 index 0000000..2dc31bf --- /dev/null +++ b/src/shaders/flat.frag @@ -0,0 +1,44 @@ +#version 300 es + +/* _______________ ,--------------------------------------------------------. + //`````````````\\ \ \ + //~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \ + //=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \ + // \\ \ \ + // \\ \ zlib licensed code at [git.nugget.fun/nugget/gunkiss] \ + // ☆ GUNKISS ☆ \\ \ \ + //_________________________\\ `--------------------------------------------------------*/ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +in vec2 uv; +uniform sampler2D base_texture; +uniform vec3 blend_min_hsv; +uniform float time; +uniform bool scroll; +out vec4 outputColor; + +/* 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 (scroll) + { + ivec2 texture_size = textureSize(base_texture, 0); + float speed = time * 35.0; + outputColor = texelFetch(base_texture, ivec2(mod(vec2(gl_FragCoord.x + speed, gl_FragCoord.y - speed), vec2(texture_size))), 0); + } + else + { + outputColor = texture(base_texture, uv); + } + /* apply blending, leaving alpha unchanged */ + outputColor.xyz = min(outputColor.xyz, hsv2rgb(blend_min_hsv)); +} diff --git a/src/shaders/flat.vert b/src/shaders/flat.vert new file mode 100644 index 0000000..cb14e68 --- /dev/null +++ b/src/shaders/flat.vert @@ -0,0 +1,24 @@ +#version 300 es + +/* _______________ ,--------------------------------------------------------. + //`````````````\\ \ \ + //~~~~~~~~~~~~~~~\\ \ by @ohsqueezy & @sleepin \ + //=================\\ \ [ohsqueezy.itch.io] [sleepin.itch.io] \ + // \\ \ \ + // \\ \ zlib licensed code at [git.nugget.fun/nugget/gunkiss] \ + // ☆ GUNKISS ☆ \\ \ \ + //_________________________\\ `--------------------------------------------------------*/ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +in vec2 in_position; +in vec2 vertex_uv; +uniform mat4 transformation; +out vec2 uv; + +void main(void) +{ + gl_Position = transformation * vec4(in_position, 0, 1); + uv = vertex_uv; +} diff --git a/src/shaders/mvp.frag b/src/shaders/mvp.frag new file mode 100644 index 0000000..af874f2 --- /dev/null +++ b/src/shaders/mvp.frag @@ -0,0 +1,76 @@ +#version 300 es + +/* >> Cakefoot by https://foam.shampoo.ooo/ << */ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +#define TRANSFORMATION_NONE 0 +#define TRANSFORMATION_SQUIRCLE 1 + +in vec2 fragment_uv; +in vec3 ex_color; +in float x_center_proximity; +in vec3 original_coordinates; +in vec3 clip_coordinates; +uniform sampler2D pudding_texture; +uniform int uv_transformation; +uniform float coordinate_bound; +out vec4 output_color; + +/* [-coordinate_bound, coordinate_bound] arbitrary box coordinates to [-1, 1] normalized coordinates */ +vec2 normalize_coordinates(vec2 coordinates) +{ + return coordinates / coordinate_bound; +} + +/* [-1, 1] box coordinates to [0, 1] UV coordinates */ +vec2 coordinates_to_uv(vec2 coordinates) +{ + return (1.0 + coordinates) / 2.0; +} + +/* 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.0); + float v_sq = pow(v, 2.0); + float rt_2 = sqrt(2.0); + float x = 0.5 * sqrt(2.0 + 2.0 * u * rt_2 + u_sq - v_sq) - 0.5 * sqrt(2.0 - 2.0 * u * rt_2 + u_sq - v_sq); + float y = 0.5 * sqrt(2.0 + 2.0 * v * rt_2 - u_sq + v_sq) - 0.5 * sqrt(2.0 - 2.0 * v * rt_2 - u_sq + v_sq); + return vec2(x, y); +} + +/* Apply color passed in from the vertex shader, compressing to one of 16 colors. Add retro effect + * by alternately darkening and lightening 2x2 pixel areas in a checker pattern. Add shadowing by + * brightening the color based on how near it is to the center in the X-dimension */ +void retro() +{ + vec3 shadowed = min(ex_color, 1.0); + float dx = abs(floor(gl_FragCoord[0]) - 480.0) / 480.0; + if (int(floor(gl_FragCoord[0] / 2.0) + floor(gl_FragCoord[1]) / 2.0) % 2 == 0) + { + output_color = vec4(shadowed * 1.2, 1); + } + else + { + output_color = vec4(shadowed * 0.7, 1); + } + output_color[0] = float(int(output_color[0] * 4.0)) / 4.0; + output_color[1] = float(int(output_color[1] * 4.0)) / 4.0; + output_color[2] = float(int(output_color[2] * 4.0)) / 4.0; + output_color *= x_center_proximity; +} + +void main() +{ + vec2 uv = fragment_uv; + // if (uv_transformation == TRANSFORMATION_SQUIRCLE) + // { + // vec2 normalized_circle_coordinates = normalize_coordinates(vec2(original_coordinates.x, original_coordinates.z)); + // uv = coordinates_to_uv(circle_to_box(normalized_circle_coordinates)); + // } + output_color = texture(pudding_texture, uv); +} diff --git a/src/shaders/mvp.vert b/src/shaders/mvp.vert new file mode 100644 index 0000000..105f79e --- /dev/null +++ b/src/shaders/mvp.vert @@ -0,0 +1,67 @@ +#version 300 es + +/* >> Cakefoot by https://foam.shampoo.ooo/ << */ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +#define PI 3.1415926535897932384626433832795 +#define AMPLITUDE 0.2 +#define PERIOD .5 +#define WAVELENGTH 2.5 +#define EFFECT_NONE 0 +#define EFFECT_SNAKE 1 +#define EFFECT_WOBBLE 2 + +in vec3 vertex_position; +in vec3 vertex_color; +in vec2 vertex_uv; +uniform mat4 mvp; +uniform float time; +uniform int effect; +out vec3 ex_color; +out float x_center_proximity; +out vec2 fragment_uv; +out vec3 original_coordinates; +out vec3 clip_coordinates; + +/* Offset X-coordinate according to the time step and Y-coordinate to create a wobble effect when run on + * flattened coordinates (after projection matrix has been applied) */ +void wobble() +{ + gl_Position.x += sin(time * 10.0) * vertex_position.y / 2.0; +} + +/* Contort the X-coordinate according the time step and Y-coordinate using the sine function. This contorts + * the model into a sine wave along the Y-axis. It also moves the sine wave along the Y-axis using the time + * step uniform. The shape can be edited by changing the defintions for amplitude (maximum distance from + * Y-axis), wavelength (length in GL model coordinates between peaks), and period (amount of time in + * seconds it takes to loop through one wavelength cycle). */ +void snake() +{ + gl_Position = vec4( + vertex_position.x + AMPLITUDE * sin(2.0 * PI / PERIOD * (time + vertex_position.y * PERIOD / WAVELENGTH)), vertex_position.yz, 1); +} + +void main() +{ + // if (effect == EFFECT_SNAKE) + // { + // snake(); + // } + // else + // { + gl_Position = vec4(vertex_position, 1); + // } + gl_Position = mvp * gl_Position; + // if (effect == EFFECT_WOBBLE) + // { + // wobble(); + // } + /* passing to fragment program */ + ex_color = vertex_color; + x_center_proximity = 1.8 - abs(vertex_position[0]); + fragment_uv = vertex_uv; + original_coordinates = vertex_position; + clip_coordinates = gl_Position.xyz; +} diff --git a/src/shaders/shader.frag b/src/shaders/shader.frag new file mode 100644 index 0000000..e141130 --- /dev/null +++ b/src/shaders/shader.frag @@ -0,0 +1,78 @@ +#version 300 es + +/* >> Cakefoot by https://foam.shampoo.ooo/ << */ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +#define TRANSFORMATION_NONE 0 +#define TRANSFORMATION_SQUIRCLE 1 + +in vec2 fragment_uv; +in vec3 ex_color; +in float x_center_proximity; +in vec3 original_coordinates; +in vec3 clip_coordinates; +uniform sampler2D model_texture; +uniform int uv_transformation; +uniform float coordinate_bound; +uniform bool texture_enabled; +out vec4 output_color; + +/* [-coordinate_bound, coordinate_bound] arbitrary box coordinates to [-1, 1] normalized coordinates */ +vec2 normalize_coordinates(vec2 coordinates) +{ + return coordinates / coordinate_bound; +} + +/* [-1, 1] box coordinates to [0, 1] UV coordinates */ +vec2 coordinates_to_uv(vec2 coordinates) +{ + return (1.0 + coordinates) / 2.0; +} + +/* 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.0); + float v_sq = pow(v, 2.0); + float rt_2 = sqrt(2.0); + float x = 0.5 * sqrt(2.0 + 2.0 * u * rt_2 + u_sq - v_sq) - 0.5 * sqrt(2.0 - 2.0 * u * rt_2 + u_sq - v_sq); + float y = 0.5 * sqrt(2.0 + 2.0 * v * rt_2 - u_sq + v_sq) - 0.5 * sqrt(2.0 - 2.0 * v * rt_2 - u_sq + v_sq); + return vec2(x, y); +} + +/* Apply color passed in from the vertex shader, compressing to one of 16 colors. Add retro effect + * by alternately darkening and lightening 2x2 pixel areas in a checker pattern. Add shadowing by + * brightening the color based on how near it is to the center in the X-dimension */ +void retro() +{ + vec3 shadowed = min(ex_color, 1.0); + float dx = abs(floor(gl_FragCoord[0]) - 480.0) / 480.0; + if (int(floor(gl_FragCoord[0] / 2.0) + floor(gl_FragCoord[1]) / 2.0) % 2 == 0) + { + output_color = vec4(shadowed * 1.2, 1); + } + else + { + output_color = vec4(shadowed * 0.7, 1); + } + output_color[0] = float(int(output_color[0] * 4.0)) / 4.0; + output_color[1] = float(int(output_color[1] * 4.0)) / 4.0; + output_color[2] = float(int(output_color[2] * 4.0)) / 4.0; + output_color *= x_center_proximity; +} + +void main() +{ + // if (uv_transformation == TRANSFORMATION_SQUIRCLE) + // { + // vec2 normalized_circle_coordinates = normalize_coordinates(vec2(original_coordinates.x, original_coordinates.z)); + // fragment_uv = coordinates_to_uv(circle_to_box(normalized_circle_coordinates)); + // } + output_color = mix(vec4(ex_color, 1), texture(model_texture, fragment_uv) * vec4(ex_color, 1), float(texture_enabled)); + // output_color = float(texture_enabled) * texture(model_texture, fragment_uv) * vec4(1, 0, 0, 0) + (1.0 - float(texture_enabled)) * vec4(1, 0, 0, 0); + // output_color = (1.0 - float(texture_enabled)) * vec4(ex_color, 1); +} diff --git a/src/shaders/shader.vert b/src/shaders/shader.vert new file mode 100644 index 0000000..105f79e --- /dev/null +++ b/src/shaders/shader.vert @@ -0,0 +1,67 @@ +#version 300 es + +/* >> Cakefoot by https://foam.shampoo.ooo/ << */ + +/* The precision declaration is required by OpenGL ES */ +precision mediump float; + +#define PI 3.1415926535897932384626433832795 +#define AMPLITUDE 0.2 +#define PERIOD .5 +#define WAVELENGTH 2.5 +#define EFFECT_NONE 0 +#define EFFECT_SNAKE 1 +#define EFFECT_WOBBLE 2 + +in vec3 vertex_position; +in vec3 vertex_color; +in vec2 vertex_uv; +uniform mat4 mvp; +uniform float time; +uniform int effect; +out vec3 ex_color; +out float x_center_proximity; +out vec2 fragment_uv; +out vec3 original_coordinates; +out vec3 clip_coordinates; + +/* Offset X-coordinate according to the time step and Y-coordinate to create a wobble effect when run on + * flattened coordinates (after projection matrix has been applied) */ +void wobble() +{ + gl_Position.x += sin(time * 10.0) * vertex_position.y / 2.0; +} + +/* Contort the X-coordinate according the time step and Y-coordinate using the sine function. This contorts + * the model into a sine wave along the Y-axis. It also moves the sine wave along the Y-axis using the time + * step uniform. The shape can be edited by changing the defintions for amplitude (maximum distance from + * Y-axis), wavelength (length in GL model coordinates between peaks), and period (amount of time in + * seconds it takes to loop through one wavelength cycle). */ +void snake() +{ + gl_Position = vec4( + vertex_position.x + AMPLITUDE * sin(2.0 * PI / PERIOD * (time + vertex_position.y * PERIOD / WAVELENGTH)), vertex_position.yz, 1); +} + +void main() +{ + // if (effect == EFFECT_SNAKE) + // { + // snake(); + // } + // else + // { + gl_Position = vec4(vertex_position, 1); + // } + gl_Position = mvp * gl_Position; + // if (effect == EFFECT_WOBBLE) + // { + // wobble(); + // } + /* passing to fragment program */ + ex_color = vertex_color; + x_center_proximity = 1.8 - abs(vertex_position[0]); + fragment_uv = vertex_uv; + original_coordinates = vertex_position; + clip_coordinates = gl_Position.xyz; +}