From 7228b2e1a2d0a8399facce3493d71a3569d250d5 Mon Sep 17 00:00:00 2001 From: mattkae Date: Fri, 23 Dec 2022 12:47:10 -0500 Subject: Improved the makefile considerably --- .gitignore | 3 +- themes/LeafParticleRender.cpp | 166 -------- themes/LeafParticleRender.h | 58 --- themes/Logger.cpp | 123 ------ themes/Logger.h | 43 --- themes/MainLoop.cpp | 31 -- themes/MainLoop.h | 29 -- themes/Makefile | 49 ++- themes/Renderer2d.cpp | 132 ------- themes/Renderer2d.h | 54 --- themes/Renderer3d.cpp | 263 ------------- themes/Renderer3d.h | 56 --- themes/Shader.cpp | 61 --- themes/Shader.h | 64 ---- themes/Snowflake.cpp | 141 ------- themes/Snowflake.h | 50 --- themes/SummerTheme.cpp | 67 ---- themes/SummerTheme.h | 23 -- themes/TreeShape.cpp | 214 ----------- themes/TreeShape.h | 74 ---- themes/WebglContext.cpp | 46 --- themes/WebglContext.h | 18 - themes/dist/output.wasm | Bin 135615 -> 135480 bytes themes/list.h | 245 ------------ themes/main.cpp | 446 ---------------------- themes/mathlib.cpp | 779 -------------------------------------- themes/mathlib.h | 181 --------- themes/src/LeafParticleRender.cpp | 166 ++++++++ themes/src/LeafParticleRender.h | 58 +++ themes/src/Logger.cpp | 123 ++++++ themes/src/Logger.h | 43 +++ themes/src/MainLoop.cpp | 31 ++ themes/src/MainLoop.h | 29 ++ themes/src/Renderer2d.cpp | 132 +++++++ themes/src/Renderer2d.h | 54 +++ themes/src/Renderer3d.cpp | 263 +++++++++++++ themes/src/Renderer3d.h | 56 +++ themes/src/Shader.cpp | 61 +++ themes/src/Shader.h | 64 ++++ themes/src/Snowflake.cpp | 135 +++++++ themes/src/Snowflake.h | 47 +++ themes/src/SummerTheme.cpp | 67 ++++ themes/src/SummerTheme.h | 23 ++ themes/src/TreeShape.cpp | 214 +++++++++++ themes/src/TreeShape.h | 74 ++++ themes/src/WebglContext.cpp | 46 +++ themes/src/WebglContext.h | 18 + themes/src/list.h | 245 ++++++++++++ themes/src/main.cpp | 445 ++++++++++++++++++++++ themes/src/mathlib.cpp | 779 ++++++++++++++++++++++++++++++++++++++ themes/src/mathlib.h | 181 +++++++++ themes/src/types.h | 16 + themes/types.h | 16 - 53 files changed, 3410 insertions(+), 3392 deletions(-) delete mode 100644 themes/LeafParticleRender.cpp delete mode 100644 themes/LeafParticleRender.h delete mode 100644 themes/Logger.cpp delete mode 100644 themes/Logger.h delete mode 100644 themes/MainLoop.cpp delete mode 100644 themes/MainLoop.h delete mode 100644 themes/Renderer2d.cpp delete mode 100644 themes/Renderer2d.h delete mode 100644 themes/Renderer3d.cpp delete mode 100644 themes/Renderer3d.h delete mode 100644 themes/Shader.cpp delete mode 100644 themes/Shader.h delete mode 100644 themes/Snowflake.cpp delete mode 100644 themes/Snowflake.h delete mode 100644 themes/SummerTheme.cpp delete mode 100644 themes/SummerTheme.h delete mode 100644 themes/TreeShape.cpp delete mode 100644 themes/TreeShape.h delete mode 100644 themes/WebglContext.cpp delete mode 100644 themes/WebglContext.h delete mode 100644 themes/list.h delete mode 100644 themes/main.cpp delete mode 100644 themes/mathlib.cpp delete mode 100644 themes/mathlib.h create mode 100644 themes/src/LeafParticleRender.cpp create mode 100644 themes/src/LeafParticleRender.h create mode 100644 themes/src/Logger.cpp create mode 100644 themes/src/Logger.h create mode 100644 themes/src/MainLoop.cpp create mode 100644 themes/src/MainLoop.h create mode 100644 themes/src/Renderer2d.cpp create mode 100644 themes/src/Renderer2d.h create mode 100644 themes/src/Renderer3d.cpp create mode 100644 themes/src/Renderer3d.h create mode 100644 themes/src/Shader.cpp create mode 100644 themes/src/Shader.h create mode 100644 themes/src/Snowflake.cpp create mode 100644 themes/src/Snowflake.h create mode 100644 themes/src/SummerTheme.cpp create mode 100644 themes/src/SummerTheme.h create mode 100644 themes/src/TreeShape.cpp create mode 100644 themes/src/TreeShape.h create mode 100644 themes/src/WebglContext.cpp create mode 100644 themes/src/WebglContext.h create mode 100644 themes/src/list.h create mode 100644 themes/src/main.cpp create mode 100644 themes/src/mathlib.cpp create mode 100644 themes/src/mathlib.h create mode 100644 themes/src/types.h delete mode 100644 themes/types.h diff --git a/.gitignore b/.gitignore index 4cff8d6..4eae390 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ *.o .vscode -.cache \ No newline at end of file +.cache +themes/dist \ No newline at end of file diff --git a/themes/LeafParticleRender.cpp b/themes/LeafParticleRender.cpp deleted file mode 100644 index 0c6fbca..0000000 --- a/themes/LeafParticleRender.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "LeafParticleRender.h" -#include "Renderer2d.h" -#include "mathlib.h" -#include "TreeShape.h" -#include "types.h" -#include - -const i32 verticesPerLeaf = 6; -const f32 leafRadius = 3.f; -const i32 fallChanceMax = 100; - -inline void updateLeaf(Vertex2D* vertices, Vector2 position, Vector4 color, f32 scale) { - f32 radius = scale * leafRadius; - Vector2 bottomLeft = Vector2(-radius, -radius) + position; - Vector2 bottomRight = Vector2(radius, -radius) + position; - Vector2 topLeft = Vector2(-radius, radius) + position; - Vector2 topRight = Vector2(radius, radius) + position; - - vertices[0] = { bottomLeft, color, Mat4x4() }; - vertices[1] = { bottomRight, color, Mat4x4() }; - vertices[2] = { topLeft, color, Mat4x4() }; - vertices[3] = { topLeft, color, Mat4x4() }; - vertices[4] = { topRight, color, Mat4x4() }; - vertices[5] = { bottomRight, color, Mat4x4() }; -} - -void LeafParticleRender::load(Renderer2d *renderer, TreeShapeLoadResult* lr) { - LeafParticleLoadData ld; - ld.numLeaves = 256; - numLeaves = ld.numLeaves; - numVertices = ld.numLeaves * verticesPerLeaf; - - updateData = new LeafParticleUpdateData[numLeaves]; - vertices = new Vertex2D[numVertices]; - - for (i32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) { - i32 randomBranch = randomIntBetween(0, lr->numBranches); - i32 randomVertex = randomIntBetween(0, 6); // TODO: Manually entering num vertices per branch. - updateData[leafIdx].vertexToFollow = &lr->updateData[randomBranch].vertices[randomVertex]; - updateData[leafIdx].fallChance = randomIntBetween(0, fallChanceMax); - updateData[leafIdx].color = Vector4(randomFloatBetween(0.3, 0.9), randomFloatBetween(0.1, 0.6), 0, 1); - updateData[leafIdx].vertexPtr = &vertices[leafIdx * verticesPerLeaf]; - updateData[leafIdx].resetTime = randomFloatBetween(4.f, 6.f); - } - - useShader(renderer->shader); - - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Vertex2D), &vertices[0], GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(renderer->attributes.position); - glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); - - glEnableVertexAttribArray(renderer->attributes.color); - glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); - - for (i32 idx = 0; idx < 4; idx++) { - glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); - glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16))); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -void LeafParticleRender::update(f32 dtSeconds) { - elapsedTimeSeconds += dtSeconds; - - // Every time the fallIntervalSeconds passes, we remove one leaf - // from the tree and send it barrelling towards the earth. - i32 fallRoll; - bool didGenerateFall = false; - if (elapsedTimeSeconds >= fallIntervalSeconds) { - fallRoll = randomIntBetween(0, fallChanceMax); - didGenerateFall = true; - elapsedTimeSeconds = 0; - } - - for (i32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) { - auto updateDataItem = &updateData[leafIdx]; - - if (didGenerateFall) { - if (updateDataItem->state == LeafParticleState::OnTree && updateDataItem->fallChance == fallRoll) { - updateDataItem->state = LeafParticleState::Falling; - updateDataItem->fallPosition = updateDataItem->vertexToFollow->position; - updateDataItem->fallVerticalVelocity = -randomFloatBetween(15.f, 25.f); - updateDataItem->fallHorizontalFrequency = randomFloatBetween(3.f, 5.f); - } - } - - switch (updateDataItem->state) { - case (LeafParticleState::Remerging): { - updateDataItem->timeElapsedSeconds += dtSeconds; - - if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) { - updateDataItem->timeElapsedSeconds = 0.f; - updateDataItem->state = LeafParticleState::OnTree; - updateDataItem->color.w = 1.f; - updateDataItem->scale = 1.f; - } - else { - updateDataItem->color.w = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime); - updateDataItem->scale = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime); - } - - updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale); - break; - } - case (LeafParticleState::OnGround): { - updateDataItem->timeElapsedSeconds += dtSeconds; - - if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) { - updateDataItem->timeElapsedSeconds = 0.f; - updateDataItem->color.w = 0.f; - updateDataItem->state = LeafParticleState::Remerging; - } - else { - updateDataItem->color.w = 1.f - (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime); - updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale); - } - break; - } - case (LeafParticleState::Falling): { - updateDataItem->timeElapsedSeconds += dtSeconds; - const f32 xPosUpdate = cosf(updateDataItem->fallHorizontalFrequency * updateDataItem->timeElapsedSeconds); - updateDataItem->fallPosition.x += xPosUpdate; - updateDataItem->fallPosition.y += updateDataItem->fallVerticalVelocity * dtSeconds; - if (updateDataItem->fallPosition.y <= 50.f) { // TODO: Hardcoded ground for now - updateDataItem->fallPosition.y = 50.f; - updateDataItem->state = LeafParticleState::OnGround; - updateDataItem->timeElapsedSeconds = 0; - updateDataItem->resetTime = randomFloatBetween(2.f, 5.f); // TODO: Hardcoded reset interval - } - updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale); - break; - } - case (LeafParticleState::OnTree): { - updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale); - break; - } - } - } -} - -void LeafParticleRender::render(Renderer2d *renderer) { - setShaderMat4(renderer->uniforms.model, model); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2D), &vertices[0]); - - glBindVertexArray(vao); - glDrawArrays(GL_TRIANGLES, 0, numVertices); - glBindVertexArray(0); -} - -void LeafParticleRender::unload() { - glDeleteVertexArrays(1, &vao); - glDeleteBuffers(1, &vbo); - delete [] vertices; - - elapsedTimeSeconds = 0; -} diff --git a/themes/LeafParticleRender.h b/themes/LeafParticleRender.h deleted file mode 100644 index 713d9f6..0000000 --- a/themes/LeafParticleRender.h +++ /dev/null @@ -1,58 +0,0 @@ -#include "Renderer2d.h" -#include "mathlib.h" -#include "types.h" - -struct TreeShapeLoadResult; - -struct LeafParticleLoadData { - Vector2 initPosition; - Vector4 initColor; - int numLeaves = 48; -}; - -enum LeafParticleState { - OnTree, - Falling, - OnGround, - Remerging -}; - -struct LeafParticleUpdateData { - LeafParticleState state = LeafParticleState::Remerging; - - Vertex2D* vertexToFollow = NULL; - Vector4 color = Vector4(1.f, 0.f, 0.f, 0.f); - f32 scale = 1.f; - - f32 timeElapsedSeconds = 0.f; - i32 fallChance = -1; - Vector2 fallPosition; - f32 fallVerticalVelocity; - f32 fallHorizontalFrequency; - - f32 resetTime = 0.f; - - Vertex2D* vertexPtr = NULL; -}; - -struct LeafParticleRender { - f32 elapsedTimeSeconds = 0.5; - f32 fallIntervalSeconds = 1.f; - - // Update data - i32 numLeaves = 0; - - LeafParticleUpdateData* updateData = NULL; - Vertex2D* vertices = NULL; - - // Render data - u32 vao; - u32 vbo; - u32 numVertices = 0; - Mat4x4 model; - - void load(Renderer2d* renderer, TreeShapeLoadResult* lr); - void update(f32 dtSeconds); - void render(Renderer2d* renderer); - void unload(); -}; \ No newline at end of file diff --git a/themes/Logger.cpp b/themes/Logger.cpp deleted file mode 100644 index 1068d88..0000000 --- a/themes/Logger.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "Logger.h" -#include -#include -#include - -namespace Logger { - LogLevel gLogLevel = LogLevel_Debug; - FILE* gFilePointer = NULL; - - void initialize(LoggerOptions options) { - setLevel(options.level); - if (options.logToFile) { -#ifdef WIN32 - fopen_s(&gFilePointer, options.filePath, "a"); -#else - gFilePointer = fopen(options.filePath, "a"); -#endif - } - } - - void setLevel(LogLevel level) { - gLogLevel = level; - } - - LogLevel getLevel() { - return gLogLevel; - } - - void printHeader(const char* levelStr, const char* fileName, int lineNumber) { - time_t t = time(0); - tm now; -#ifdef WIN32 - localtime_s(&now, &t); -#else - now = *localtime(&t); -#endif - - printf("%s:%d [%d-%d-%d %d:%d:%d] %s: ", fileName, lineNumber, (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); - if (gFilePointer != NULL) { - fprintf(gFilePointer, "[%d-%d-%d %d:%d:%d] %s: ", (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); - } - } - - void logInternal(const char* file, int lineNumber,LogLevel level, const char* format, va_list args) { - if (level < gLogLevel) { - return; - } - - - const char* levelStr; - switch (level) { - case LogLevel_Debug: - levelStr = "Debug"; - break; - case LogLevel_Info: - levelStr = "Info"; - break; - case LogLevel_Warn: - levelStr = "Warning"; - break; - case LogLevel_Error: - levelStr = "Error"; - break; - default: - levelStr = "Unknown"; - break; - } - - if (gFilePointer != NULL) { - va_list fileArgs; - va_copy(fileArgs, args); - vfprintf(gFilePointer, format, fileArgs); - fprintf(gFilePointer, "\n"); - } - - printHeader(levelStr, file, lineNumber); - - vprintf(format, args); - printf("\n"); - } - - void doLog(const char* file, int lineNumber,LogLevel level, const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, level, format, args); - va_end(args); - } - - void doDebug(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Debug, format, args); - va_end(args); - } - - void doInfo(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Info, format, args); - va_end(args); - } - - void doWarning(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Warn, format, args); - va_end(args); - } - - void doError(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Error, format, args); - va_end(args); - } - - void free() { - if (gFilePointer) { - fclose(gFilePointer); - gFilePointer = NULL; - } - } -} diff --git a/themes/Logger.h b/themes/Logger.h deleted file mode 100644 index 7596b6f..0000000 --- a/themes/Logger.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef LOGGER_H -#define LOGGER_H - -#include - -enum LogLevel { - LogLevel_Debug = 0, - LogLevel_Info = 1, - LogLevel_Warn = 2, - LogLevel_Error = 3 -}; - -struct LoggerOptions { - LogLevel level = LogLevel_Debug; - bool logToFile = false; - const char* filePath = "debug.log"; -}; - -namespace Logger { - void initialize(LoggerOptions options); - void setLevel(LogLevel level); - LogLevel getLevel(); - void doLog(const char* file, int lineNumber, LogLevel level, const char* format, ...); - void doDebug(const char* file, int lineNumber, const char* format, ...); - void doInfo(const char* file, int lineNumber, const char* format, ...); - void doWarning(const char* file, int lineNumber, const char* format, ...); - void doError(const char* file, int lineNumber, const char* format, ...); - void free(); -}; - -#if WIN32 -#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) -#else -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#endif - -#define logger_log(level, format, ...) Logger::doLog(__FILENAME__, __LINE__, level, format, ## __VA_ARGS__) -#define logger_debug(format, ...) Logger::doDebug(__FILENAME__, __LINE__, format, ## __VA_ARGS__) -#define logger_info(format, ...) Logger::doInfo(__FILENAME__, __LINE__, format, ## __VA_ARGS__) -#define logger_warning(format, ...) Logger::doWarning(__FILENAME__, __LINE__, format, ## __VA_ARGS__) -#define logger_error(format, ...) Logger::doError(__FILENAME__, __LINE__, format, ## __VA_ARGS__) - -#endif diff --git a/themes/MainLoop.cpp b/themes/MainLoop.cpp deleted file mode 100644 index 09aa643..0000000 --- a/themes/MainLoop.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "MainLoop.h" -#include -#include - -EM_BOOL loop(double time, void* loop) { - MainLoop* mainLoop = (MainLoop*) loop; - if (!mainLoop->isRunning) { - return false; - } - - if (mainLoop->lastTime == 0) { - mainLoop->lastTime = time; - return true; - } - - long deltaTime = time - mainLoop->lastTime; - mainLoop->lastTime = time; - mainLoop->elapsedTime += deltaTime; - mainLoop->numFrames++; - float deltaTimeSeconds = static_cast(deltaTime) / 1000.f; - - if (mainLoop->elapsedTime >= 1000.0) { - printf("FPS: %d\n", mainLoop->numFrames); - - mainLoop->elapsedTime = 0.0; - mainLoop->numFrames = 0; - } - - mainLoop->updateFunc(deltaTimeSeconds, NULL); - return true; -} \ No newline at end of file diff --git a/themes/MainLoop.h b/themes/MainLoop.h deleted file mode 100644 index 2573bb8..0000000 --- a/themes/MainLoop.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -EM_BOOL loop(double time, void* loop); - -struct MainLoop { - bool isRunning = false; - double lastTime = 0, elapsedTime = 0; - int numFrames = 0; - void (*updateFunc)(float dtSeconds, void *userData); - - void run(void (*cb)(float dtSeconds, void *userData)) { - isRunning = true; - lastTime = 0; - elapsedTime = 0; - numFrames = 0; - updateFunc = cb; - - emscripten_request_animation_frame_loop(loop, this); - } - - void stop() { - isRunning = false; - } -}; \ No newline at end of file diff --git a/themes/Makefile b/themes/Makefile index 8bdc04e..bfe42a3 100644 --- a/themes/Makefile +++ b/themes/Makefile @@ -1,17 +1,44 @@ -CXX = emcc -EXE = dist/output.js -SRCS = $(wildcard *.cpp) -OBJS = $(patsubst %.cpp,%.o,$(SRCS)) +TARGET_EXEC ?= output.js + +BUILD_DIR ?= ./dist +SRC_DIRS ?= ./src + +CC := emcc +CXX := emcc +SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s) +OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) +DEPS := $(OBJS:.o=.d) + +INC_DIRS := $(shell find $(SRC_DIRS) -type d) +INC_FLAGS := $(addprefix -I,$(INC_DIRS)) LDFLAGS = -s ALLOW_MEMORY_GROWTH=1 -s USE_WEBGL2=1 -s FULL_ES3=1 -s WASM=1 -s NO_EXIT_RUNTIME=1 -s FETCH -all: build $(EXE) +CPPFLAGS ?= $(INC_FLAGS) -MMD -MP + +$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS) + $(CC) $(OBJS) -o $@ $(LDFLAGS) + +# assembly +$(BUILD_DIR)/%.s.o: %.s + $(MKDIR_P) $(dir $@) + $(AS) $(ASFLAGS) -c $< -o $@ -$(EXE): $(OBJS) - echo $(OBJS) - $(CXX) -o $(EXE) $(OBJS) $(LDFLAGS) +# c source +$(BUILD_DIR)/%.c.o: %.c + $(MKDIR_P) $(dir $@) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ -build: - @mkdir -p dist +# c++ source +$(BUILD_DIR)/%.cpp.o: %.cpp + $(MKDIR_P) $(dir $@) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ + + +.PHONY: clean clean: - rm -rf dist && rm *.o \ No newline at end of file + $(RM) -r $(BUILD_DIR) + +-include $(DEPS) + +MKDIR_P ?= mkdir -p diff --git a/themes/Renderer2d.cpp b/themes/Renderer2d.cpp deleted file mode 100644 index c303e83..0000000 --- a/themes/Renderer2d.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "Renderer2d.h" -#include "Shader.h" -#include "WebglContext.h" -#include "mathlib.h" -#include - -// Note: In the 'transform' attribute, the transform.x is the scale, -// transform.y is the rotation, and transform.zw is the translation. -const char* Vertex2DShader = -"attribute vec2 position; \n" -"attribute vec4 color; \n" -"attribute mat4 vMatrix; \n" -"uniform mat4 projection; \n" -"uniform mat4 model; \n" -"varying lowp vec4 VertexColor; \n" -"void main() { \n" -" vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); \n" -" gl_Position = fragmentPosition; \n" -" VertexColor = color; \n" -"}"; - -const char* renderer2dFragmentShader = -"varying lowp vec4 VertexColor; \n" -"void main() { \n" -" gl_FragColor = VertexColor; \n" -"}"; - -void Renderer2d::load(WebglContext* inContext) { - context = inContext; - printf("Compiling Renderer2d shader...\n"); - shader = loadShader(Vertex2DShader, renderer2dFragmentShader); - - useShader(shader); - attributes.position = getShaderAttribute(shader, "position"); - attributes.color = getShaderAttribute(shader, "color"); - attributes.vMatrix = getShaderAttribute(shader, "vMatrix"); - uniforms.projection = getShaderUniform(shader, "projection"); - uniforms.model = getShaderUniform(shader, "model"); - projection = Mat4x4().getOrthographicMatrix(0, context->width, 0, context->height); - - printf("Renderer2d shader compiled.\n"); -} - -void Renderer2d::render() { - projection = Mat4x4().getOrthographicMatrix(0, context->width, 0, context->height); - - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glDepthMask(GL_TRUE); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); - glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w); - glClearDepth(1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - useShader(shader); - setShaderMat4(uniforms.projection, projection); -} - -void Renderer2d::unload() { - glClearColor(0.f, 0.f, 0.f, 0.f); - glClear(GL_COLOR_BUFFER_BIT); - glDeleteProgram(shader); -} - - -void Mesh2D::load(Vertex2D* inVertices, u32 inNumVertices, Renderer2d* renderer) { - ebo = 0; - numVertices = inNumVertices; - useShader(renderer->shader); - - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, inNumVertices * sizeof(Vertex2D), &inVertices[0], GL_STATIC_DRAW); - - glEnableVertexAttribArray(renderer->attributes.position); - glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); - - glEnableVertexAttribArray(renderer->attributes.color); - glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); - - for (i32 idx = 0; idx < 4; idx++) { - glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); - glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16))); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - - -void Mesh2D::load(Vertex2D* vertices, - u32 numVertices, - u32* indices, - u32 inNumIndices, - Renderer2d* renderer) { - load(vertices, numVertices, renderer); - glBindVertexArray(vao); - glGenBuffers(1, &ebo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, inNumIndices * sizeof(u32), &indices[0], GL_STATIC_DRAW); - numIndices = inNumIndices; - glBindVertexArray(0); -} - -void Mesh2D::render(Renderer2d* renderer, GLenum drawType) { - setShaderMat4(renderer->uniforms.model, model); - - if (ebo == 0) { - glBindVertexArray(vao); - glDrawArrays(drawType, 0, numVertices); - glBindVertexArray(0); - } - else { - glBindVertexArray(vao); - glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, 0); - glBindVertexArray(0); - } -} - -void Mesh2D::unload() { - glDeleteVertexArrays(1, &vao); - glDeleteBuffers(1, &vbo); - if (ebo != 0) { - glDeleteBuffers(1, &ebo); - ebo = 0; - } - vao = 0; - vbo = 0; -} diff --git a/themes/Renderer2d.h b/themes/Renderer2d.h deleted file mode 100644 index 909f088..0000000 --- a/themes/Renderer2d.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "WebglContext.h" -#include "types.h" -#include "Shader.h" -#include "mathlib.h" - -struct WebglContext; - -struct Renderer2d { - WebglContext* context = NULL; - Mat4x4 projection; - u32 shader; - Vector4 clearColor; - - struct { - i32 position; - i32 color; - i32 vMatrix; - } attributes; - - struct { - i32 projection; - i32 model; - } uniforms; - - void load(WebglContext* context); - void render(); - void unload(); -}; - -struct Vertex2D { - Vector2 position; - Vector4 color; - Mat4x4 vMatrix; -}; - -struct Mesh2D { - u32 vao; - u32 vbo; - u32 ebo = 0; - u32 numVertices = 0; - u32 numIndices = 0; - Mat4x4 model; - - void load(Vertex2D* vertices, u32 numVertices, Renderer2d* renderer); - void load(Vertex2D* vertices, - u32 numVertices, - u32* indices, - u32 numIndices, - Renderer2d* renderer); - void render(Renderer2d* renderer, GLenum drawType = GL_TRIANGLES); - void unload(); -}; diff --git a/themes/Renderer3d.cpp b/themes/Renderer3d.cpp deleted file mode 100644 index 5f9ce88..0000000 --- a/themes/Renderer3d.cpp +++ /dev/null @@ -1,263 +0,0 @@ -#include "Renderer3d.h" -#include "Shader.h" -#include "list.h" -#include "mathlib.h" -#include "WebglContext.h" -#include "Logger.h" -#include - -// Note: In the 'transform' attribute, the transform.x is the scale, -// transform.y is the rotation, and transform.zw is the translatiob. -const char* vertexShader = - "attribute vec4 position; \n" - "attribute vec4 color; \n" - "attribute vec4 normal; \n" - "uniform mat4 projection; \n" - "uniform mat4 view; \n" - "uniform mat4 model; \n" - "varying lowp vec4 VertexColor; \n" - "varying lowp vec4 VertexNormal; \n" - "void main() { \n" - " vec4 fragmentPosition = projection * view * model * position; \n" - " gl_Position = fragmentPosition; \n" - " VertexColor = color; \n" - " VertexNormal = normal; \n" - "}"; - -const char* fragmentShader = - "varying lowp vec4 VertexColor; \n" - "varying lowp vec4 VertexNormal; \n" - "void main() { \n" - " const lowp vec3 lightDirection = vec3(0.0, 1.0, 0.0);\n" - " gl_FragColor = vec4(VertexColor.xyz * dot(VertexNormal.xyz, lightDirection), 1); \n" - "}"; - -EM_BOOL onScreenSizeChanged_3D(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) { - Renderer3D* renderer = (Renderer3D*)userData; - - EMSCRIPTEN_RESULT result = emscripten_set_canvas_element_size( renderer->context->query, uiEvent->documentBodyClientWidth, uiEvent->documentBodyClientHeight); - if (result != EMSCRIPTEN_RESULT_SUCCESS) { - logger_error("Failed to resize element at query: %s\n", renderer->context->query); - } - //renderer->projection = Mat4x4().getOrthographicMatrix(0, renderer->context->width, 0, renderer->context->height); - - return true; -} - -void Renderer3D::load(WebglContext* inContext) { - context = inContext; - printf("Compiling Renderer2d shader...\n"); - shader = loadShader(vertexShader, fragmentShader); - - useShader(shader); - attributes.position = getShaderAttribute(shader, "position"); - attributes.color = getShaderAttribute(shader, "color"); - attributes.normal = getShaderAttribute(shader, "normal"); - uniforms.projection = getShaderUniform(shader, "projection"); - uniforms.view = getShaderUniform(shader, "view"); - uniforms.model = getShaderUniform(shader, "model"); - projection = Mat4x4().getPerspectiveProjection(0.1, 1000.f, 0.872f, static_cast(context->width) / static_cast(context->height)); - view = Mat4x4().getLookAt({ 0, 25, 75 }, { 0, 15, 0 }, { 0, 1, 0 }); - - logger_info("Renderer2d shader compiled.\n"); - - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, onScreenSizeChanged_3D); -} - -void Renderer3D::render() { - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glDepthMask(GL_TRUE); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); - glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w); - glClearDepth(1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - useShader(shader); - setShaderMat4(uniforms.projection, projection); - setShaderMat4(uniforms.view, view); -} - -void Renderer3D::unload() { - glClearColor(0.f, 0.f, 0.f, 0.f); - glClear(GL_COLOR_BUFFER_BIT); - glDeleteProgram(shader); -} - -enum LineType { - LineType_None, - LineType_Comment, - LineType_mtl, - LineType_v, - LineType_f, - LineType_Unsupported -}; - -struct LineItem { - LineType type = LineType_None; - - i32 idx = 0; - - union { - f32 vertices[3]; - i32 indices[3]; - } v; -}; - -inline i32 readPastSpaces(i32 i, const char* content) { - while (content[i] == ' ') i++; - return i; -} - -inline i32 readPastLine(i32 i, const char* content) { - while (content[i] != '\n' && content[i] != '\0') i++; - return i; -} - -inline i32 readToken(i32 i, const char* content, char* output) { - i32 tidx = 0; - i = readPastSpaces(i, content); - while (content[i] != ' ' && content[i] != '\n' && content[i] != '\0') { - output[tidx] = content[i]; - i++; - tidx++; - } - output[tidx] = '\0'; - return i; -} - -Mesh3d Mesh3d_fromObj(Renderer3D* renderer, const char* content, const i32 len) { - Mesh3d result; - result.vertices.allocate(2048); - result.indices.allocate(2048); - - LineItem lt; - lt.type = LineType_None; - i32 lineNumber = 0; - i32 i = 0; - while (content[i] != '\0') { - i = readPastSpaces(i, content); - if (lt.type == LineType_None) { - lineNumber++; - char type[32]; - i = readToken(i, content, type); - - if (strncmp(type, "#", 1) == 0) { - lt.type = LineType_Comment; - } - else if (strncmp(type, "mtllib", 6) == 0) { - lt.type = LineType_mtl; - } - else if (strncmp(type, "v", 1) == 0) { - lt.type = LineType_v; - } - else if (strncmp(type, "f", 1) == 0) { - lt.type = LineType_f; - } - else { - i++; - //lt.type = LineType_Unsupported; - //logger_error("Unknown type %s, %d", type, lineNumber); - } - } - else { - char buffer[32]; - switch (lt.type) { - case LineType_mtl: - i = readToken(i, content, buffer); - break; - case LineType_v: { - while (content[i] != '\n' && content[i] != '\0') { - i = readToken(i, content, buffer); - lt.v.vertices[lt.idx] = atof(buffer); - lt.idx++; - } - - float fColor = randomFloatBetween(0.8, 1); - result.vertices.add({ - Vector4(lt.v.vertices[0], lt.v.vertices[1], lt.v.vertices[2], 1.f), - Vector4(fColor, fColor, fColor, 1) - }); - break; - } - case LineType_f: { - while (content[i] != '\n' && content[i] != '\0') { - i = readToken(i, content, buffer); - lt.v.indices[lt.idx] = atoi(buffer); - lt.idx++; - } - - auto v1idx = lt.v.indices[0] - 1; - auto v2idx = lt.v.indices[1] - 1; - auto v3idx = lt.v.indices[2] - 1; - - result.indices.add(v1idx); - result.indices.add(v2idx); - result.indices.add(v3idx); - - auto& v1 = result.vertices[v1idx]; - auto& v2 = result.vertices[v2idx]; - auto& v3 = result.vertices[v3idx]; - Vector3 normal = (v1.position - v2.position).cross(v1.position - v3.position).toVector3().normalize(); - v1.normal = normal; - v2.normal = normal; - v3.normal = normal; - break; - } - default: - i = readPastLine(i, content); - break; - } - - lt = LineItem(); - } - } - - printf("Completed Mesh3d loading.\n"); - result.load(renderer); - return result; -} - -void Mesh3d::load(Renderer3D* renderer) { - glGenVertexArrays(1, &vao); - glGenBuffers(1, &vbo); - glGenBuffers(1, &ebo); - - glBindVertexArray(vao); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, vertices.numElements * sizeof(Vertex3d), &vertices.data[0], GL_STATIC_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.numElements * sizeof(GLuint), &indices.data[0], GL_STATIC_DRAW); - - // Position - glEnableVertexAttribArray(renderer->attributes.position); - glVertexAttribPointer(renderer->attributes.position, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex3d), (GLvoid *)0); - - // Color - glEnableVertexAttribArray(renderer->attributes.color); - glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex3d), (GLvoid *)offsetof(Vertex3d, color)); - - // Normal - glEnableVertexAttribArray(renderer->attributes.normal); - glVertexAttribPointer(renderer->attributes.normal, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex3d), (GLvoid *)offsetof(Vertex3d, normal)); - - glBindVertexArray(0); -} - -void Mesh3d::unload() { - if (vao) glDeleteVertexArrays(1, &vao); - if (vbo) glDeleteBuffers(1, &vbo); - if (ebo) glDeleteBuffers(1, &ebo); - vertices.deallocate(); - indices.deallocate(); -} - -void Mesh3d::render(Renderer3D* renderer) { - setShaderMat4(renderer->uniforms.model, model); - - glBindVertexArray(vao); - glDrawElements(GL_TRIANGLES, indices.numElements, GL_UNSIGNED_INT, 0); - glBindVertexArray(0); -} diff --git a/themes/Renderer3d.h b/themes/Renderer3d.h deleted file mode 100644 index 7e89c93..0000000 --- a/themes/Renderer3d.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef RENDERER3D_H -#define RENDERER3D_H -#include "mathlib.h" -#include "list.h" -#include "types.h" -#include - -struct Renderer3D; - -struct Vertex3d { - Vector4 position; - Vector4 color; - Vector4 normal; -}; - -struct Mesh3d { - u32 vao; - u32 vbo; - u32 ebo; - matte::List vertices; - matte::List indices; - Mat4x4 model; - - void load(Renderer3D* renderer); - void render(Renderer3D* renderer); - void unload(); -}; - -struct WebglContext; -struct Renderer3D { - WebglContext* context = NULL; - Mat4x4 projection; - Mat4x4 view; - u32 shader; - Vector4 clearColor; - - struct { - i32 position; - i32 color; - i32 normal; - } attributes; - - struct { - i32 projection; - i32 view; - i32 model; - } uniforms; - - void load(WebglContext* context); - void render(); - void unload(); -}; - -Mesh3d Mesh3d_fromObj(Renderer3D* renderer, const char* content, const i32 len); - -#endif diff --git a/themes/Shader.cpp b/themes/Shader.cpp deleted file mode 100644 index 5f2b00e..0000000 --- a/themes/Shader.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "Shader.h" -#include - -GLuint loadIndividualShader(GLenum shaderType, const GLchar* cCode) { - GLuint shader = glCreateShader(shaderType); - glShaderSource(shader, 1, &cCode, 0); - glCompileShader(shader); - GLint success; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar infoLog[512]; - glGetShaderInfoLog(shader, 512, 0, infoLog); - printf("Failed to load shader: %s, Shader =%s\n", infoLog, cCode); - return 0; - } - - return shader; -} - -void attachShaders(Shader& retVal, const GLchar* vertexShader, const GLchar* fragmentShader) { - GLuint vertex = 0, fragment = 0, geometry = 0; - if (vertexShader) { - vertex = loadIndividualShader(GL_VERTEX_SHADER, vertexShader); - glAttachShader(retVal, vertex); - } - - if (fragmentShader) { - fragment = loadIndividualShader(GL_FRAGMENT_SHADER, fragmentShader); - glAttachShader(retVal, fragment); - } - - glLinkProgram(retVal); - GLint isLinked = 0; - glGetProgramiv(retVal, GL_LINK_STATUS, (int*)&isLinked); - if (isLinked == GL_FALSE) { - GLint maxLength = 0; - glGetProgramiv(retVal, GL_INFO_LOG_LENGTH, &maxLength); - - // The maxLength includes the NULL character - GLchar* infoLog = new GLchar[maxLength]; - glGetProgramInfoLog(retVal, maxLength, &maxLength, infoLog); - glDeleteProgram(retVal); - printf("Error. Could not initialize shader with vertex=%s, error=%s\n", vertexShader, infoLog); - delete []infoLog; - } - - if (vertexShader) - glDeleteShader(vertex); - if (fragmentShader) - glDeleteShader(fragment); -} - -Shader loadShader(const GLchar* vertexShader, const GLchar* fragmentShader) { - Shader retVal; - retVal = glCreateProgram(); - - attachShaders(retVal, vertexShader, fragmentShader); - useShader(retVal); - - return retVal; -} diff --git a/themes/Shader.h b/themes/Shader.h deleted file mode 100644 index bc81764..0000000 --- a/themes/Shader.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include -#include -#include "mathlib.h" - -typedef GLuint Shader; - -Shader loadShader(const GLchar* vertexShader, const GLchar* fragmentShader); - -inline GLint getShaderUniform(const Shader& shader, const GLchar *name) { - GLint uid = glGetUniformLocation(shader, name); - if (uid < 0) { - return -1; - } - return uid; -} - -inline GLint getShaderAttribute(const Shader& shader, const GLchar *name) { - printf("Getting attribute for shader, name: %d, %s\n", shader, name); - GLint uid = glGetAttribLocation(shader, name); - if (uid < 0) { - printf("Unable to get attribute %s for shader %d\n", name, shader); - return -1; - } - return uid; -} - -inline void useShader(const Shader& shader) { - glUseProgram(shader); -} - -inline void setShaderFloat(GLint location, GLfloat value) { - glUniform1f(location, value); -} - -inline void setShaderInt(GLint location, GLint value) { - glUniform1i(location, value); -} - -inline void setShaderUint(GLint location, GLuint value) { - glUniform1ui(location, value); -} - -inline void setShaderVec2(GLint location, const Vector2& value) { - glUniform2f(location, value.x, value.y); -} - -inline void setShaderMat4(GLint location, const Mat4x4& matrix) { - glUniformMatrix4fv(location, 1, GL_FALSE, matrix.m); -} - -inline void setShaderBVec3(GLint location, bool first, bool second, bool third) { - glUniform3i(location, first, second, third); -} - -inline void setShaderBVec4(GLint location, bool first, bool second, bool third, bool fourth) { - glUniform4i(location, first, second, third, fourth); -} - -inline void setShaderBool(GLint location, bool value) { - glUniform1i(location, value); -} diff --git a/themes/Snowflake.cpp b/themes/Snowflake.cpp deleted file mode 100644 index 1cb056a..0000000 --- a/themes/Snowflake.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "Snowflake.h" -#include "Renderer2d.h" -#include "mathlib.h" -#include "list.h" -#include - -const Vector4 snowColor = Vector4(1.0, 0.98, 0.98, 1); - -inline void generateSnowflakeShape(matte::List* vertices, i32 numArms, f32 radius, f32 innerRadius) { - f32 dx = ((2 * PI) / numArms) / 3.0; - for (i32 centerIdx = 0; centerIdx < (3 * numArms); centerIdx+=3) { - f32 degreeStart = dx * centerIdx; - f32 degreeEnd = dx * (centerIdx + 1); - - f32 cosStart = cosf(degreeStart); - f32 cosEnd = cosf(degreeEnd); - - f32 sinStart = sinf(degreeStart); - f32 sinEnd = sinf(degreeEnd); - - Vector2 leftEnd = Vector2(radius * cosStart, radius * sinStart); - Vector2 rightEnd = Vector2(radius * cosEnd, radius * sinEnd); - Vector2 diff = (rightEnd - leftEnd) / 2.0; - Vector2 leftStart = Vector2(-diff.x, -diff.y) + Vector2(innerRadius * cosStart, innerRadius * sinStart); - Vector2 rightStart = diff + Vector2(innerRadius * cosEnd, innerRadius * sinEnd); - - vertices->add({ leftStart, snowColor, Mat4x4() }); - vertices->add({ leftEnd, snowColor, Mat4x4() }); - vertices->add({ rightEnd, snowColor, Mat4x4() }); - vertices->add({ rightEnd, snowColor, Mat4x4() }); - vertices->add({ rightStart, snowColor, Mat4x4() }); - vertices->add({ leftStart, snowColor, Mat4x4() }); - } -} - -inline void initFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud) { - ud->vtxIdx = renderer->vertices.numElements; - generateSnowflakeShape(&renderer->vertices, randomIntBetween(4, 16), randomFloatBetween(8.f, 16.f), randomFloatBetween(2.f, 6.f)); - ud->numVertices = renderer->vertices.numElements - ud->vtxIdx; - ud->velocity = Vector2(randomFloatBetween(-10, 10), randomFloatBetween(-100, -85)); - ud->position = Vector2(randomFloatBetween(0, renderer->xMax), randomFloatBetween(renderer->yMax, renderer->yMax + 256)); -} - -void SnowflakeParticleRenderer::load(SnowflakeLoadParameters params, Renderer2d* renderer) { - spawnIntervalSeconds = params.spawnIntervalSeconds; - numSnowflakes = params.maxSnowflakes; - - updateData = new SnowflakeUpdateData[params.maxSnowflakes]; - - xMax = static_cast(renderer->context->width); - yMax = static_cast(renderer->context->height); - - vertices.deallocate(); - vertices.growDynamically = true; - - // Initialize each snow flake with its shape - for (i32 s = 0; s < numSnowflakes; s++) { - auto ud = &updateData[s]; - initFlake(this, ud); - } - - useShader(renderer->shader); - - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, vertices.numElements * sizeof(Vertex2D), &vertices.data[0], GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(renderer->attributes.position); - glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); - - glEnableVertexAttribArray(renderer->attributes.color); - glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); - - for (i32 idx = 0; idx < 4; idx++) { - i32 offset = (4 * sizeof(f32)) * idx; - glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); - glVertexAttribPointer(renderer->attributes.vMatrix + idx, - 4, - GL_FLOAT, - GL_FALSE, - sizeof(Vertex2D), - (GLvoid *)(offsetof(Vertex2D, vMatrix) + offset)); - //glVertexAttribDivisor(renderer->attributes.vMatrix + idx, 1); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} - -inline void updateFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud, i32 s, f32 dtSeconds, bool addWind) { - if (addWind) ud->velocity += renderer->windSpeed; - ud->position += ud->velocity * dtSeconds; - - Mat4x4 m = Mat4x4().translateByVec2(ud->position); - for (i32 v = ud->vtxIdx; v < (ud->vtxIdx + ud->numVertices); v++) { - renderer->vertices.data[v].vMatrix = m; - } -} - -void SnowflakeParticleRenderer::update(f32 dtSeconds) { - timeUntilNextSpawnSeconds -= dtSeconds; - if (timeUntilNextSpawnSeconds < 0) { - timeUntilNextSpawnSeconds = spawnIntervalSeconds; - } - - bool addWind = false; - timeUntilNextWindSeconds -= dtSeconds; - if (timeUntilNextWindSeconds < 0) { - timeUntilNextWindSeconds = windIntervalSeconds; - windSpeed = Vector2(randomFloatBetween(-10, 10), randomFloatBetween(-10, 0)); - addWind = true; - } - - for (i32 s = 0; s < numSnowflakes; s++) { - SnowflakeUpdateData* ud = &updateData[s]; - updateFlake(this, ud, s, dtSeconds, addWind); - } -} - -void SnowflakeParticleRenderer::render(Renderer2d* renderer) { - setShaderMat4(renderer->uniforms.model, model); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.numElements * sizeof(Vertex2D), &vertices.data[0]); - - glBindVertexArray(vao); - glDrawArrays(GL_TRIANGLES, 0, vertices.numElements); - glBindVertexArray(0); -} - -void SnowflakeParticleRenderer::unload() { - glDeleteVertexArrays(1, &vao); - glDeleteBuffers(1, &vbo); - vao = 0; - vbo = 0; - vertices.deallocate(); - delete [] updateData; -} diff --git a/themes/Snowflake.h b/themes/Snowflake.h deleted file mode 100644 index 5ad2670..0000000 --- a/themes/Snowflake.h +++ /dev/null @@ -1,50 +0,0 @@ -#include "types.h" -#include "mathlib.h" -#include "list.h" - -struct Renderer2d; -struct Vertex2D; - -struct SnowflakeLoadParameters { - i32 maxSnowflakes = 1000; - f32 rateOfSnowfall = 0.1f; - Vector2 flakeV0 = { 0, 1 }; - f32 flakeSize = 5.f; - f32 flakeSizeDeviation = 1.f; - Vector4 snowColor = { 0.8, 0.8, 0.8, 1.0 }; - f32 spawnIntervalSeconds = 0.3; - f32 windIntervalSeconds = 1.5; -}; - -struct SnowflakeUpdateData { - Vector2 v0; - Vector2 velocity; - Vector2 position; - f32 rotation; - bool onGround = false; - - i32 vtxIdx = 0; - i32 numVertices = 0; -}; - -struct SnowflakeParticleRenderer { - f32 xMax = 0; - f32 yMax = 0; - f32 spawnIntervalSeconds = 0.3; - f32 windIntervalSeconds = 1.5; - i32 numSnowflakes = 0; - Vector2 windSpeed; - SnowflakeUpdateData* updateData; - f32 timeUntilNextSpawnSeconds = 0; - f32 timeUntilNextWindSeconds = 0; - - u32 vao; - u32 vbo; - Mat4x4 model; - matte::List vertices; - - void load(SnowflakeLoadParameters params, Renderer2d* renderer); - void update(f32 dtSeconds); - void render(Renderer2d* renderer); - void unload(); -}; diff --git a/themes/SummerTheme.cpp b/themes/SummerTheme.cpp deleted file mode 100644 index 20bb310..0000000 --- a/themes/SummerTheme.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "SummerTheme.h" -#include "Renderer2d.h" -#include "list.h" -#include "mathlib.h" -#include - -void SummerTheme::load(Renderer2d* renderer) { - renderer->clearColor = Vector4(0, 181, 286, 255.f).toNormalizedColor(); - sun.sectors = 180; - sun.radius = renderer->context->width / 4.f; - sun.load(renderer); -} - -void SummerTheme::update(f32 dtSeconds) { - sun.update(dtSeconds); -} - -void SummerTheme::render(Renderer2d* renderer) { - sun.render(renderer); -} - -void SummerTheme::unload() { - sun.unload(); -} - -void Sun::load(Renderer2d* renderer) { - matte::List vertices; - matte::List indices; - Vector4 sunColor = Vector4(249, 215, 28, 255).toNormalizedColor(); - vertices.add({ Vector2(0, 0), sunColor, Mat4x4() }); - - f32 radiansPerSector = (2.f * PI) / sectors; - for (i32 i = 0; i <= sectors; i++) { - f32 radians = radiansPerSector * i; - f32 cosAngle = cosf(radians); - f32 sinAngle = sinf(radians); - Vector2 vertex = Vector2(radius * cosAngle, radius * sinAngle); - vertices.add({ vertex, sunColor, Mat4x4() }); - - u32 first = i; - u32 second = 0; - u32 third = i + 1; - indices.add(first); - indices.add(second); - indices.add(third); - } - - mesh.load(&vertices.data[0], vertices.numElements, &indices.data[0], indices.numElements, renderer); - mesh.model = Mat4x4().translateByVec2(Vector2(renderer->context->width / 2.f, renderer->context->height / 2.f)); - vertices.deallocate(); - indices.deallocate(); -} - -void Sun::update(f32 dtSeconds) { - -} - -void Sun::render(Renderer2d* renderer) { - setShaderMat4(renderer->uniforms.model, mesh.model); - glBindVertexArray(mesh.vao); - glDrawElements(GL_TRIANGLES, mesh.numIndices, GL_UNSIGNED_INT, 0); - glBindVertexArray(0); -} - -void Sun::unload() { - mesh.unload(); -} diff --git a/themes/SummerTheme.h b/themes/SummerTheme.h deleted file mode 100644 index 1d9093a..0000000 --- a/themes/SummerTheme.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "types.h" -#include "Renderer2d.h" -#include - -struct Sun { - f32 radius = 20.f; - i32 sectors = 180; - Mesh2D mesh; - - void load(Renderer2d* renderer); - void update(f32 dtSeconds); - void render(Renderer2d* renderer); - void unload(); -}; - -struct SummerTheme { - Sun sun; - void load(Renderer2d* renderer); - void update(f32 dtSeconds); - void render(Renderer2d* renderer); - void unload(); -}; diff --git a/themes/TreeShape.cpp b/themes/TreeShape.cpp deleted file mode 100644 index a3ae8f7..0000000 --- a/themes/TreeShape.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "TreeShape.h" -#include "mathlib.h" -#include -#include -#include -#include - -void TreeBranchLoadData::fillVertices(Vertex2D* vertices, int branchTier) { - bottomLeft = Vector2 { position.x - width / 2.f, position.y }.rotateAround(rotation, position); - bottomRight = Vector2 { position.x + width / 2.f, position.y }.rotateAround(rotation, position); - topLeft = (Vector2 { position.x - width / 2.f, position.y + height }).rotateAround(rotation, position); - topRight = (Vector2 { position.x + width / 2.f, position.y + height }).rotateAround(rotation, position); - - topMidpoint = topLeft + (topRight - topLeft) / 2.f; - - vertices[0] = { bottomLeft, color}; - vertices[1] = { bottomRight, color}; - vertices[2] = { topLeft, color}; - vertices[3] = { topLeft, color}; - vertices[4] = { topRight, color}; - vertices[5] = { bottomRight, color}; -}; - -TreeShapeLoadResult TreeShape::load(Renderer2d* renderer) { - srand ( time(NULL) ); - - timeElapsedSeconds = 0; - - TreeLoadData ld; - - numBranches = pow(ld.divisionsPerBranch, ld.numBranchLevels + 1); - numVertices = 6 * numBranches; - - TreeBranchLoadData* generationData = new TreeBranchLoadData[numBranches]; - updateData = new TreeBranchUpdateData[numBranches]; - vertices = new Vertex2D[numVertices]; - - // The load result will contain information that we can pass on to our leaf renderer. - TreeShapeLoadResult lr; - lr.lowerBounds = Vector2(FLT_MAX, FLT_MAX); - lr.upperBounds = Vector2(FLT_MIN, FLT_MIN); - lr.updateData = updateData; - lr.numBranches = numBranches; - i32 branchIndex = 0; - createBranch(&ld, generationData, numBranches, &branchIndex, 0, ld.trunkWidth, ld.trunkHeight, Vector2 { 300.f, 50.f }, 0, NULL, vertices, &lr); - - useShader(renderer->shader); - - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); - - glGenBuffers(1, &vbo); - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Vertex2D), &vertices[0], GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(renderer->attributes.position); - glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); - - glEnableVertexAttribArray(renderer->attributes.color); - glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); - - for (i32 idx = 0; idx < 4; idx++) { - glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); - glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16))); - glVertexAttribDivisor(renderer->attributes.vMatrix + idx, 1); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - delete [] generationData; - - return lr; -} - -const f32 ninetyDegreeRotation = PI / 2.f; - -void TreeShape::createBranch(TreeLoadData* ld, TreeBranchLoadData* generationData, i32 numBranches, i32* branchIndex, - i32 branchLevel, f32 width, f32 height, Vector2 position, f32 rotation, - TreeBranchUpdateData* parent, Vertex2D* vertices, TreeShapeLoadResult* lr) { - TreeBranchLoadData* branchLoadData = &generationData[*branchIndex]; - branchLoadData->width = width; - branchLoadData->height = height; - branchLoadData->position = position; - branchLoadData->rotation = rotation; - branchLoadData->fillVertices(&vertices[(*branchIndex) * 6], branchLevel); - - // Fil in the bounds for the LeafRenderer later. - if (branchLoadData->topMidpoint.x > lr->upperBounds.x) { - lr->upperBounds.x = branchLoadData->topMidpoint.x; - } - if (branchLoadData->topMidpoint.y > lr->upperBounds.y) { - lr->upperBounds.y = branchLoadData->topMidpoint.y; - } - if (branchLoadData->topMidpoint.x < lr->lowerBounds.x) { - lr->lowerBounds.x = branchLoadData->topMidpoint.x; - } - if (branchLoadData->topMidpoint.y < lr->lowerBounds.y) { - lr->lowerBounds.y = branchLoadData->topMidpoint.y; - } - - TreeBranchUpdateData* branchUpdateData = &updateData[*branchIndex]; - branchUpdateData->tier = branchLevel; - branchUpdateData->periodOffset = randomFloatBetween(0.f, 2.f * PI); - branchUpdateData->period = randomFloatBetween(3.f, 5.f); - branchUpdateData->amplitude = randomFloatBetween(0.01f, 0.05f); - branchUpdateData->branchToFollow = parent; - branchUpdateData->vertices = &vertices[(*branchIndex) * 6]; - - if (branchLevel == ld->numBranchLevels) { - return; - } - - for (int division = 0; division < ld->divisionsPerBranch; division++) { - // Weight between [0, 1] - float weight = static_cast(division) / static_cast(ld->divisionsPerBranch - 1); - - // Normalize the weight between [-1, 1] - f32 normalizedWeight = (0.5f - (weight)) * 2.f; - - // We want a rotation that takes the current rotation of the branch, and averages it between the two branches. - f32 branchRotationAmount = randomFloatBetween(PI / 8.f, PI / 3.f); - f32 branchRotation = branchLoadData->rotation + (normalizedWeight * branchRotationAmount); - - // Since trees are taller vertically, we will find a normalized value that describes how far the direction is from - // being horizontal. If it is closer to 1, we will make the branch taller on average. - f32 verticalHeightScaler = (fabs(fabs(branchRotation) - ninetyDegreeRotation) / ninetyDegreeRotation) * 0.1; - f32 branchWidth = width * randomFloatBetween(ld->trunkWidthScalerMin, ld->trunkWidthScalerMax); - f32 branchHeight = height * randomFloatBetween(ld->trunkHeightScalerMin + verticalHeightScaler, ld->trunkHeightScalerMax + verticalHeightScaler); - - - // We want the branch to start within the previous branch, so we drop it down into it based off of the rotation. - Vector2 branchOffsetVertical = Vector2{ 0, branchWidth }.rotate(branchRotation); - - Vector2 branchPosition = branchLoadData->topLeft + ((branchLoadData->topRight - branchLoadData->topLeft) * weight) - branchOffsetVertical; // Position of branch along the top of the parent branch - - (*branchIndex)++; - createBranch(ld, generationData, numBranches, branchIndex, branchLevel + 1, branchWidth, branchHeight, branchPosition, branchRotation, branchUpdateData, vertices, lr); - } -} - -void TreeShape::update(f32 dtSeconds) { - timeElapsedSeconds += dtSeconds; - - for (i32 bIdx = 0; bIdx < numBranches; bIdx++) { - TreeBranchUpdateData* branchUpdataData = &updateData[bIdx]; - - // Fade in simulation. We fade in based on the tier. - f32 animationStart = (branchUpdataData->tier * animateStaggerPerTier); - f32 animationEnd = animationStart + animateTimePerTier; - - f32 alpha = 0.f; - if (timeElapsedSeconds < animationStart) { - alpha = 0.f; - } - else if (timeElapsedSeconds > animationEnd) { - alpha = 1.f; - } - else { - alpha = (1.f - (animationEnd - timeElapsedSeconds)) / animateTimePerTier; - } - - i32 startParentIndex = bIdx * 6; - - branchUpdataData->currentOffset.x = branchUpdataData->amplitude * cosf(branchUpdataData->periodOffset + branchUpdataData->period * timeElapsedSeconds); - branchUpdataData->currentOffset.y = branchUpdataData->amplitude * sinf(branchUpdataData->periodOffset + branchUpdataData->period * timeElapsedSeconds); - - if (branchUpdataData->branchToFollow != NULL) { - branchUpdataData->currentOffset += branchUpdataData->branchToFollow->currentOffset; - - // The root of the branch only moves according to the change of the end of the parent. - branchUpdataData->vertices[0].color.w = alpha; - branchUpdataData->vertices[0].position += branchUpdataData->branchToFollow->currentOffset; - branchUpdataData->vertices[1].color.w = alpha; - branchUpdataData->vertices[1].position += branchUpdataData->branchToFollow->currentOffset; - branchUpdataData->vertices[5].color.w = alpha; - branchUpdataData->vertices[5].position += branchUpdataData->branchToFollow->currentOffset; - } - else { - branchUpdataData->vertices[0].color.w = alpha; - branchUpdataData->vertices[1].color.w = alpha; - branchUpdataData->vertices[5].color.w = alpha; - } - - - branchUpdataData->vertices[2].color.w = alpha; - branchUpdataData->vertices[2].position += branchUpdataData->currentOffset; - branchUpdataData->vertices[3].color.w = alpha; - branchUpdataData->vertices[3].position += branchUpdataData->currentOffset; - branchUpdataData->vertices[4].color.w = alpha; - branchUpdataData->vertices[4].position += branchUpdataData->currentOffset; - } -} - -void TreeShape::render(Renderer2d* renderer) { - setShaderMat4(renderer->uniforms.model, model); - - glBindBuffer(GL_ARRAY_BUFFER, vbo); - glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2D), &vertices[0]); - - glBindVertexArray(vao); - glDrawArrays(GL_TRIANGLES, 0, numVertices); - glBindVertexArray(0); -} - -void TreeShape::unload() { - glDeleteVertexArrays(1, &vao); - glDeleteBuffers(1, &vbo); - delete[] vertices; - delete [] updateData; - timeElapsedSeconds = 0; - vertices = NULL; - updateData = NULL; -} diff --git a/themes/TreeShape.h b/themes/TreeShape.h deleted file mode 100644 index 32b00d3..0000000 --- a/themes/TreeShape.h +++ /dev/null @@ -1,74 +0,0 @@ -#include "Renderer2d.h" -#include "types.h" -#include "mathlib.h" - -struct TreeLoadData { - f32 trunkHeight = 96.f; // Height of the trunk start - f32 trunkWidth = 32.f; // Width of the trunk start - f32 trunkHeightScalerMin = 0.7f; - f32 trunkHeightScalerMax = 0.8f; - f32 trunkWidthScalerMin = 0.35f; - f32 trunkWidthScalerMax = 0.75f; - i32 divisionsPerBranch = 2; // How many branches to split into at each branch split - i32 numBranchLevels = 8; // How many branch levels to display -}; - -struct TreeBranchLoadData { - f32 width = 0.f; - f32 height = 0.f; - Vector2 position; // Center point - f32 rotation = 0; // How much we are rotated off of the center point in radians - Vector4 color = Vector4(101,56,24, 1.f).toNormalizedColor(); - - // Calculated while filling in vertices - Vector2 bottomLeft; - Vector2 bottomRight; - Vector2 topLeft; - Vector2 topRight; - Vector2 topMidpoint; - - void fillVertices(Vertex2D* vertices, int branchTier); -}; - -struct TreeBranchUpdateData { - i32 tier = 0; - f32 periodOffset = 0; - f32 period = 0; - f32 amplitude = 0; - Vector2 currentOffset; - Vertex2D* vertices = NULL; - TreeBranchUpdateData* branchToFollow = NULL; -}; - -struct TreeShapeLoadResult { - Vector2 lowerBounds; - Vector2 upperBounds; - Vector2 center; - TreeBranchUpdateData* updateData; - u32 numBranches = 0; -}; - -struct TreeShape { - // Update data - TreeBranchUpdateData* updateData = NULL; - Vertex2D* vertices = NULL; - f32 timeElapsedSeconds = 0.f; - f32 animateTimePerTier = 1.f; - f32 animateStaggerPerTier = 0.2f; - u32 numBranches = 0; - - // Render data - u32 vao; - u32 vbo; - u32 numVertices = 0; - Mat4x4 model; - - TreeShapeLoadResult load(Renderer2d* renderer); - void createBranch(TreeLoadData* ld, TreeBranchLoadData* branchList, i32 numBranches, - i32* branchIndex, i32 branchLevel, f32 width, f32 height, - Vector2 position, f32 rotation, TreeBranchUpdateData* parent, Vertex2D* vertices, TreeShapeLoadResult* lr); - void update(f32 dtSeconds); - void render(Renderer2d* renderer); - void unload(); -}; - diff --git a/themes/WebglContext.cpp b/themes/WebglContext.cpp deleted file mode 100644 index df49c2d..0000000 --- a/themes/WebglContext.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "WebglContext.h" -#include - - -EM_BOOL onResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) { - WebglContext* context = (WebglContext*)userData; - - f64 inWidth, inHeight; - emscripten_get_element_css_size(context->query, &inWidth, &inHeight); - - context->width = static_cast(inWidth); - context->height = static_cast(inHeight); - - return true; -} - -void WebglContext::init(const char* inQuery) { - strcpy(query, inQuery); - f64 inWidth, inHeight; - emscripten_get_element_css_size(query, &inWidth, &inHeight); - width = static_cast(inWidth); - height = static_cast(inHeight); - emscripten_set_canvas_element_size( query, width, height); - - EmscriptenWebGLContextAttributes attrs; - emscripten_webgl_init_context_attributes(&attrs); - - attrs.enableExtensionsByDefault = 1; - attrs.majorVersion = 3; - attrs.minorVersion = 0; - - context = emscripten_webgl_create_context(query, &attrs); - makeCurrentContext(); - - glClearColor(0, 0, 0, 0.0f); - - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, onResize); -}; - -void WebglContext::makeCurrentContext() { - emscripten_webgl_make_context_current(context); -}; - -void WebglContext::destroy() { - emscripten_webgl_destroy_context(context); -} diff --git a/themes/WebglContext.h b/themes/WebglContext.h deleted file mode 100644 index 1956092..0000000 --- a/themes/WebglContext.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once -#include "types.h" -#include -#include -#include -#include -#include - -struct WebglContext { - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context; - f32 width = 800; - f32 height = 600; - char query[128];; - - void init(const char* inQuery); - void makeCurrentContext(); - void destroy() ; -}; diff --git a/themes/dist/output.wasm b/themes/dist/output.wasm index 33cd231..02f03e6 100755 Binary files a/themes/dist/output.wasm and b/themes/dist/output.wasm differ diff --git a/themes/list.h b/themes/list.h deleted file mode 100644 index 25b236a..0000000 --- a/themes/list.h +++ /dev/null @@ -1,245 +0,0 @@ -#pragma once -#include -#include -#include "Logger.h" - -#define FOREACH(list) \ - for (i32 idx = 0; idx < list.numElements; idx++) \ - if (auto value = &list[idx]) \ - -namespace matte { - template - struct List { - T* data = nullptr; - size_t capacity = 0; - size_t numElements = 0; - bool growDynamically = true; - - void allocate(size_t size); - void add(T& element); - void add(T&& element); - void add(T* element); - bool grow(size_t newSize); - void set(T& value, size_t index); - void set(T&& value, size_t index); - void set(T* value, size_t index); - void remove(size_t index); - void clear(); - void deallocate(); - bool isEmpty() { - return data == nullptr || numElements == 0; - } - T& getValue(int index) const; - T& operator[](int idx) const; - void binarySort(int (*f)(const T& first, const T& second)); - void setFromArray(T* arry, int size) { - allocate(size); - memcpy(data, arry, size * sizeof(T)); - numElements = size; - } - void remove(int index) { - if (index >= numElements) { - logger_error("Cannot remove element at index: %d", index); - return; - } - - - if (index == numElements - 1) { - numElements--; - return; - } - - memmove(&data[index], &data[index + 1], sizeof(T) * (numElements - index)); - numElements--; - } - }; - - template - void List::allocate(size_t size) { - if (size == 0 || size == capacity) { - numElements = 0; - return; - } - - if (data != nullptr) { - deallocate(); - } - - data = static_cast(malloc(sizeof(T) * size)); - capacity = size; - numElements = 0; - } - - template - bool List::grow(size_t newSize) { - if (!growDynamically) { - logger_error("Cannot grow list: growDynamically is disabled"); - return false; - } - - if (newSize == 0) { - logger_error("Cannot grow list: newSize is zero!"); - return false; - } - - T* newData = static_cast(malloc(sizeof(T) * newSize)); - - if (data != nullptr) { - memcpy(newData, data, numElements * sizeof(T)); - delete data; - } - - data = newData; - capacity = newSize; - return true; - } - - template - void List::set(T& value, size_t index) { - if (index >= capacity && !grow(index * 2)) { - return; - } - - memcpy(&data[index], value, sizeof(T)); - } - - template - void List::set(T&& value, size_t index) { - if (index >= capacity && !grow(index * 2)) { - return; - } - - data[index] = value; - } - - template - void List::set(T* value, size_t index) { - if (index >= capacity && !grow(index * 2)) { - return; - } - - memcpy(&data[index], value, sizeof(T)); - } - - template - void List::add(T* element) { - if (data == nullptr) { - allocate(2); - } - - if (element == nullptr) { - logger_error("Element not defined"); - return; - } - - size_t newNumElements = numElements + 1; - if (newNumElements > capacity) { - if (!grow(2 * capacity)) { - logger_error("Trying to add to list but unable to grow the array"); - return; - } - } - - memcpy(&data[numElements], element, sizeof(T)); - numElements = newNumElements; - } - - template - void List::add(T& element) { - if (data == nullptr) { - allocate(2); - } - - size_t newNumElements = numElements + 1; - if (newNumElements > capacity) { - if (!grow(2 * capacity)) { - logger_error("Trying to add to list but unable to grow the array"); - return; - } - } - - memcpy(&data[numElements], &element, sizeof(T)); - numElements = newNumElements; - } - - template - void List::add(T&& element) { - if (data == nullptr) { - allocate(2); - } - - size_t newNumElements = numElements + 1; - if (newNumElements > capacity) { - if (!grow(2 * capacity)) { - logger_error("Trying to add to list but unable to grow the array"); - return; - } - } - - memcpy(&data[numElements], &element, sizeof(T)); - numElements = newNumElements; - } - - template - void List::remove(size_t idx) { - if (idx >= numElements) { - logger_error("Index is outside of the list: %d >= %d", idx, numElements); - return; - } - - for (; idx < numElements - 1; idx++) { - data[idx] = data[idx + 1]; - } - - numElements--; - } - - template - void List::deallocate() { - if (data != nullptr) { - free(data); - data = nullptr; - } - - capacity = 0; - numElements = 0; - } - - template - void List::clear() { - numElements = 0; - } - - template - T& List::getValue(int idx) const { - return data[idx]; - } - - template - T& List::operator[](int idx) const { - return data[idx]; - } - - template - void List::binarySort(int (*f)(const T& first, const T& second)) { - if (data == nullptr) { - return; - } - - for (size_t idx = 0; idx < numElements - 1; idx++) { - int minIdx = idx; - T firstValue = data[idx]; - - for (int innerIdx = idx + 1; innerIdx < numElements; innerIdx++) {\ - T secondValue = data[innerIdx]; - if (f(data[idx], data[innerIdx]) > 0) { - minIdx= innerIdx; - } - } - - T temp = data[minIdx]; - memmove(&data[minIdx], &data[idx], sizeof(T)); - memmove(&data[idx], &temp, sizeof(T)); - } - } -} diff --git a/themes/main.cpp b/themes/main.cpp deleted file mode 100644 index 4e82ca0..0000000 --- a/themes/main.cpp +++ /dev/null @@ -1,446 +0,0 @@ -#include "WebglContext.h" -#include "MainLoop.h" -#include "Renderer2d.h" -#include "Renderer3d.h" -#include "mathlib.h" -#include "types.h" -#include "TreeShape.h" -#include "SummerTheme.h" -#include "LeafParticleRender.h" -#include "Snowflake.h" -#include -#include - - -enum Theme { - Default = 0, - Autumn, - Winter, - Spring, - Summer -}; - -struct AutumnTheme { - TreeShape tree; - LeafParticleRender leafParticles; - - void load(Renderer2d* renderer); - void update(f32 dtSeconds); - void render(Renderer2d* renderer); - void unload(); -}; - -struct WinterTheme { - SnowflakeParticleRenderer spr; - - void load(Renderer2d* renderer); - void update(f32 dtSeconds); - void render(Renderer2d* renderer); - void unload(); -}; - -enum class BunnyAnimationState { - Loading = 0, - Loaded, - PreHop, - Hopping, - Idle -}; - -struct SpringTheme { - BunnyAnimationState state; - f32 bunnySpeed = 5.f; - Vector3 bunnyPosition = Vector3(0, 0, 0); - Vector3 bunnyTarget = Vector3(0, 0, 0); - Vector3 hopIncrement = Vector3(0, 0, 0); - - f32 numHops = 0; - f32 hopCount = 0; - f32 bunnyHopAnimationTimer = 0.f; - f32 stateTimer = 0.f; - f32 bunnyRotation = 0.f; - f32 targetRotation = 0.f; - - Mesh3d bunnyMesh; - - void load(Renderer3D* renderer); - void update(f32 dtSeconds); - void render(Renderer3D* renderer); - void unload(); -}; - -void load(Theme theme); -void unload(); -void update(f32 dtSeconds, void* userData); -EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); -EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); - -WebglContext context; -Renderer2d renderer2d; -Renderer3D renderer3d; -MainLoop mainLoop; -Theme activeTheme = Theme::Default; -AutumnTheme autumnTheme; -WinterTheme winterTheme; -SpringTheme springTheme; -SummerTheme summerTheme; - -int main() { - context.init("#theme_canvas"); - emscripten_set_click_callback("#theme_button_default", NULL, false, selectNone); - emscripten_set_click_callback("#theme_button_autumn", NULL, false, selectAutumn); - emscripten_set_click_callback("#theme_button_winter", NULL, false, selectWinter); - emscripten_set_click_callback("#theme_button_spring", NULL, false, selectSpring); - emscripten_set_click_callback("#theme_button_summer", NULL, false, selectSummer); - - return 0; -} - -// -- Scene loading, updating, and unloading logic -void load(Theme theme) { - if (activeTheme == theme) { - printf("This theme is already active.\n"); - return; - } - - unload(); // Try and unload before we load, so that we start fresh - - activeTheme = theme; - mainLoop.run(update); - - switch (activeTheme) { - case Theme::Autumn: - renderer2d.load(&context); - autumnTheme.load(&renderer2d); - break; - case Theme::Winter: - renderer2d.load(&context); - winterTheme.load(&renderer2d); - break; - case Theme::Spring: - renderer3d.load(&context); - springTheme.load(&renderer3d); - break; - case Theme::Summer: - renderer2d.load(&context); - summerTheme.load(&renderer2d); - break; - default: - break; - } -} - -void update(f32 dtSeconds, void* userData) { - // -- Update - switch (activeTheme) { - case Theme::Autumn: - autumnTheme.update(dtSeconds); - break; - case Theme::Winter: - winterTheme.update(dtSeconds); - break; - case Theme::Spring: - springTheme.update(dtSeconds); - break; - case Theme::Summer: - summerTheme.update(dtSeconds); - break; - default: - break; - } - - // -- Render - switch (activeTheme) { - case Theme::Autumn: - renderer2d.render(); - autumnTheme.render(&renderer2d); - break; - case Theme::Winter: - renderer2d.render(); - winterTheme.render(&renderer2d); - break; - case Theme::Spring: - renderer3d.render(); - springTheme.render(&renderer3d); - break; - case Theme::Summer: - renderer2d.render(); - summerTheme.render(&renderer2d); - break; - default: - break; - } -} - -void unload() { - switch (activeTheme) { - case Theme::Autumn: - autumnTheme.unload(); - break; - case Theme::Winter: - winterTheme.unload(); - break; - case Theme::Spring: - springTheme.unload(); - break; - case Theme::Summer: - summerTheme.unload(); - break; - default: - break; - } - - activeTheme = Theme::Default; - if (mainLoop.isRunning) { - mainLoop.stop(); - renderer2d.unload(); - renderer3d.unload(); - } -} - -// -- HTML5 callbacks -EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Default theme selected\n"); - unload(); - return true; -} - -EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Autumn theme selected\n"); - load(Theme::Autumn); - return true; -} - -EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Winter theme selected\n"); - load(Theme::Winter); - return true; -} - -EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Spring theme selected\n"); - load(Theme::Spring); - return true; -} - -EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { - printf("Summer theme selected\n"); - load(Theme::Summer); - return true; -} - -// -- Autumn theme -void AutumnTheme::load(Renderer2d* renderer) { - renderer->clearColor = Vector4(252, 210, 153, 255).toNormalizedColor(); - auto lr = tree.load(renderer); - leafParticles.load(renderer, &lr); -} - -void AutumnTheme::update(f32 dtSeconds) { - tree.update(dtSeconds); - leafParticles.update(dtSeconds); -} - -void AutumnTheme::render(Renderer2d* renderer) { - tree.render(renderer); - leafParticles.render(renderer); -} - -void AutumnTheme::unload() { - tree.unload(); - leafParticles.unload(); -} - -// -- Winter theme -void WinterTheme::load(Renderer2d* renderer) { - renderer->clearColor = Vector4(200, 229, 239, 255).toNormalizedColor(); - SnowflakeLoadParameters lp; - lp.spawnIntervalSeconds = 0.05; - spr.load(lp, renderer); -} - -void WinterTheme::update(f32 dtSeconds) { - spr.update(dtSeconds); -} - -void WinterTheme::render(Renderer2d* renderer) { - spr.render(renderer); -} - -void WinterTheme::unload() { - spr.unload(); -} - -// -- Spring theme -void onBunnySuccess(emscripten_fetch_t *fetch) { - springTheme.state = BunnyAnimationState::Loaded; - printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url); - const i32 len = fetch->numBytes; - springTheme.bunnyMesh = Mesh3d_fromObj(&renderer3d, fetch->data, len); - // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1]; - emscripten_fetch_close(fetch); // Free data associated with the fetch. -} - -void onBunnyFail(emscripten_fetch_t *fetch) { - printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status); - emscripten_fetch_close(fetch); // Also free data on failure. -} - -void SpringTheme::load(Renderer3D* renderer) { - springTheme.state = BunnyAnimationState::Loading; - renderer->clearColor = Vector4(160, 231, 160, 255.f).toNormalizedColor(); - - emscripten_fetch_attr_t attr; - emscripten_fetch_attr_init(&attr); - strcpy(attr.requestMethod, "GET"); - attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; - attr.onsuccess = onBunnySuccess; - attr.onerror = onBunnyFail; - emscripten_fetch(&attr, "themes/resources/bunny.obj"); -} - -inline Vector3 bunnyLerp(Vector3& start, Vector3& target, f32 t) { - t = 3 * t *t - 2 * t * t * t; - return start + ((target - start) * t); -} - -inline f32 verticalHopLerp(f32 start, f32 target, f32 t) { - f32 ogt = t; - t = 3 * t *t - 2 * t * t * t; - if (ogt >= 0.5f) t = 1.f - t; - return start + ((target - start) * t); -} - -inline f32 rotationLerp(f32 start, f32 target, f32 t) { - return start + ((target - start) * t); -} - -void SpringTheme::update(f32 dtSeconds) { - switch (state) { - case BunnyAnimationState::Loading: return; - case BunnyAnimationState::Loaded: - state = BunnyAnimationState::Idle; - stateTimer = 0.f; - bunnyHopAnimationTimer = 0.f; - break; - case BunnyAnimationState::Idle: { - bunnyHopAnimationTimer += dtSeconds; - const f32 HOP_FREQUENCY = 6.f; - - if (bunnyHopAnimationTimer > stateTimer) { - state = BunnyAnimationState::PreHop; - f32 xDir = 1; - f32 yDir = 1; - if (bunnyTarget.x > 0) xDir = -1; - if (bunnyTarget.z > 0) yDir = -1; - bunnyTarget = bunnyPosition + Vector3(randomFloatBetween(0, xDir * 25), 0, randomFloatBetween(0, yDir * 25)); - auto direction = (bunnyTarget - bunnyPosition); - auto distance = direction.length(); - direction = direction.normalize(); - numHops = ceil(distance / HOP_FREQUENCY); - hopCount = 0; - - targetRotation = PI - atan2(direction.y, direction.x); - stateTimer = ((bunnyTarget - bunnyPosition).length() / bunnySpeed) / numHops; - bunnyHopAnimationTimer = 0.f; - hopIncrement = (bunnyTarget - bunnyPosition) / numHops; - } - break; - } - case BunnyAnimationState::PreHop: { - const f32 ROTATION_TIME = 0.5f; - bunnyHopAnimationTimer += dtSeconds; - f32 current = bunnyRotation + (targetRotation - bunnyRotation) * (bunnyHopAnimationTimer / ROTATION_TIME); - bunnyMesh.model = Mat4x4().rotate(0, current, 0).translate(bunnyPosition); - - if (bunnyHopAnimationTimer > ROTATION_TIME) { - bunnyRotation = targetRotation; - bunnyHopAnimationTimer = 0; - state = BunnyAnimationState::Hopping; - } - break; - } - case BunnyAnimationState::Hopping: { - bunnyHopAnimationTimer += dtSeconds; - f32 t = bunnyHopAnimationTimer / stateTimer; - - Vector3 nextPosition = bunnyPosition + hopIncrement; - auto renderPos = bunnyLerp(bunnyPosition, nextPosition, t); - if ((renderPos - nextPosition).length() < 0.01f) { - hopCount += 1; - bunnyHopAnimationTimer = 0.f; - bunnyPosition = nextPosition; - } - - renderPos.y = verticalHopLerp(0.f, 4.f, t); - - const f32 RMAX = PI / 16.f; - f32 zRotation = 0; - f32 start = 0.f; - f32 end = PI / 8.f; - f32 startTime = 0.f; - f32 endTime = 0.f; - bool disableRot = false; - - if (t >= 0.9f) { - disableRot = true; - } - else if (t >= 0.7f) { - start = -RMAX; - end = 0.f; - startTime = 0.7f; - endTime = 0.9f; - } - else if (t >= 0.50f) { - start = 0.f; - end = -RMAX; - startTime = 0.50f; - endTime = 0.70f; - } - else if (t >= 0.40f) { - disableRot = true; - } - else if (t >= 0.20f) { - start = RMAX; - end = 0.f; - startTime = 0.20f; - endTime = 0.40f; - } - else { - start = 0.f; - end = RMAX; - startTime = 0.f; - endTime = 0.20f; - } - - - if (!disableRot) { - f32 totalTime = endTime - startTime; - zRotation = rotationLerp(start, end, (totalTime - (endTime - t)) / totalTime); - } - - bunnyMesh.model = Mat4x4().getZRotationMatrix(zRotation).rotate(0, bunnyRotation, 0).translate(renderPos); - if (hopCount == numHops) { - bunnyPosition = bunnyTarget; - bunnyHopAnimationTimer = 0.f; - state = BunnyAnimationState::Idle; - stateTimer = randomFloatBetween(0.5f, 1.f); - } - break; - } - } -} - -void SpringTheme::render(Renderer3D* renderer) { - renderer->render(); - if (state != BunnyAnimationState::Loading) { - bunnyMesh.render(renderer); - } -} - -void SpringTheme::unload() { - bunnyMesh.unload(); -} diff --git a/themes/mathlib.cpp b/themes/mathlib.cpp deleted file mode 100644 index 9e86833..0000000 --- a/themes/mathlib.cpp +++ /dev/null @@ -1,779 +0,0 @@ -// mathlib.cpp -// -// Created by Matt Kosarek -// -// A file containing some common math functionality that I find -// useful in my projects. I don't like that I don't know what's happening -// in glm, so I wrote most of this myself. All mistakes are my own. -// - -#include "mathlib.h" -#include - -// *************************************** -// Vector2 -Vector2::Vector2() { } - -Vector2::Vector2(float inX, float inY) { - x = inX; - y = inY; -} - -Vector2 getRandomNormalVector2() { - Vector2 retval = { - static_cast(rand()) / static_cast(RAND_MAX), - static_cast(rand()) / static_cast(RAND_MAX) - }; - - return retval.normalize(); -} - -Vector2 Vector2::operator+(Vector2 other) { - return { x + other.x, y + other.y }; -} - -Vector2& Vector2::operator+=(Vector2 other) { - x += other.x; - y += other.y; - return *this; -} - -Vector2 Vector2::operator-(Vector2 other) { - return { x - other.x, y - other.y }; -} - -Vector2 Vector2::operator*(float s) { - return { x * s, y * s }; -} - -Vector2 Vector2::operator/(float s) { - return { x / s, y / s }; -} - -float Vector2::dot(Vector2 other) { - return x * other.x + y * other.y; -} - -float Vector2::length() { - return sqrtf(x * x + y * y); -} - -Vector2 Vector2::normalize() { - float len = length(); - float inverseLength = len == 0 ? 1.0 : 1.0 / len; - - return { x * inverseLength, y * inverseLength }; -} - -Vector2 Vector2::negate() { - return { -x, -y }; -} - -Vector2 Vector2::getPerp() { - return { y, -x }; -} - -Vector2 Vector2::rotate(float angle) { - return { - x * cosf(angle) - y * sinf(angle), - x * sinf(angle) + y * cosf(angle) - }; -} - -Vector2 Vector2::rotateAround(float angle, const Vector2& other) { - Vector2 point = { x - other.x, y - other.y }; - point = { - point.x * cosf(angle) - point.y * sinf(angle), - point.x * sinf(angle) + point.y * cosf(angle) - }; - point = point + other; - return point; -} - -void Vector2::printDebug(const char* name) { - printf("%s=Vector2(%f, %f)\n", name, x, y); -} - - -// *************************************** -// Vector3 -Vector3::Vector3() { }; - -Vector3::Vector3(float value) { - x = value; - y = value; - z = value; -} - -Vector3::Vector3(float inX, float inY, float inZ) { - x = inX; - y = inY; - z = inZ; -} - -float Vector3::length() { - return sqrtf(x * x + y * y + z * z); -} - -float Vector3::dot(const Vector3& other) { - return x * other.x + y * other.y + z * other.z; -} - -Vector3 Vector3::scale(float scalar) { - return { - x * scalar, - y * scalar, - z * scalar - }; -} - -Vector3 Vector3::add(const Vector3& other) { - return { - x + other.x, - y + other.y, - z + other.z - }; -} - -Vector3 Vector3::subtract(const Vector3& other) { - return { - x - other.x, - y - other.y, - z - other.z - }; -} - -Vector3 Vector3::negate() { - return { - -x, - -y, - -z - }; -} - -Vector3 Vector3::cross(const Vector3& other) { - return { - y * other.z - z * other.y, - z * other.x - x * other.z, - x * other.y - y * other.x - }; -} - -Vector3 Vector3::normalize() { - float len = 1.f / length(); - return { - x * len, - y * len, - z * len - }; -} - -Vector3 Vector3::operator+(const Vector3& v2) { - return add(v2); -} - -Vector3& Vector3::operator+=(Vector3 other) { - x += other.x; - y += other.y; - z += other.z; - return *this; -} - -Vector3 Vector3::operator-(const Vector3& v2) { - return subtract(v2); -} - -Vector3 Vector3::operator-() { - return negate(); -} - -Vector3 Vector3::operator*(float value) { - return scale(value); -} - -Vector3 Vector3::operator/(const Vector3& v2) { - return { - x / v2.x, - y / v2.y, - z / v2.z - }; -} - -Vector3 Vector3::operator*(const Vector3& v2) { - return { - x * v2.x, - y * v2.y, - z * v2.z - }; -} - -float Vector3::operator [](int index) { - switch (index) { - case 0: - return x; - case 1: - return y; - case 2: - return z; - default: - return 0; - } -} - -void Vector2::operator=(const Vector4& other) { - x = other.x; - y = other.y; -} - -void Vector3::printDebug(const char* name) { - printf("%s=Vector3(%f, %f, %f)\n", name, x, y, z); -} - - -// *************************************** -// Vector4 -Vector4::Vector4() { } - -Vector4::Vector4(float value) { - x = value; - y = value; - z = value; - w = value; -} - -Vector4::Vector4(float inX, float inY, float inZ, float inW) { - x = inX; - y = inY; - z = inZ; - w = inW; -} - -Vector4::Vector4(Vector2& v) { - x = v.x; - y = v.y; - z = 0; - w = 1; -} - -Vector4::Vector4(Vector3& v) { - x = v.x; - y = v.y; - z = v.z; - w = 1; -} - -Vector4 Vector4::fromColor(float r, float g, float b, float a) { - float scale = 1.f / 255.f; - return { r * scale, g * scale, b * scale, a * scale }; -} - -Vector4 Vector4::toNormalizedColor() { - return fromColor(x, y, z, w); -} - - -Vector3 Vector4::toVector3() { - return { x, y, z }; -} - -float Vector4::length() { - return sqrtf(x * x + y * y + z * z + w * w); -} - -Vector4 Vector4::normalize() { - float len = length(); - float inverseLength = len == 0 ? 1.0 : 1.0 / len; - - return { x * inverseLength, y * inverseLength, z * inverseLength, w * inverseLength }; -} - -float Vector4::dot(const Vector4& other) { - return x * other.x + y * other.y + z * other.z + w * other.w; -} - -Vector4 Vector4::scale(float scalar) { - return { - x * scalar, - y * scalar, - z * scalar, - w * scalar - }; -} - -Vector4 Vector4::add(const Vector4& other) { - return { - x + other.x, - y + other.y, - z + other.z, - w + other.w - }; -} - -Vector4 Vector4::subtract(const Vector4& other) { - return { - x - other.x, - y - other.y, - z - other.z, - w - other.w - }; -} - -Vector4 Vector4::negate() { - return { -x, -y, -z, -w }; -} - -Vector4 Vector4::cross(const Vector4& other) { - return { - y * other.z - z * other.y, - z * other.x - x * other.z, - x * other.y - y * other.x, - 1.f - }; -} - -Vector4 lerp(Vector4 start, Vector4 end, float t) { - return (end - start) * t + start; -} - -void Vector4::operator=(const Vector2& v2) { - x = v2.x; - y = v2.y; - z = 0; - w = 1; -} - -void Vector4::operator=(const Vector3& v2) { - x = v2.x; - y = v2.y; - z = v2.z; - w = 1; -} - -Vector4 Vector4::operator+(const Vector4& v2) { - return add(v2); -} - -Vector4 Vector4::operator-(const Vector4& v2) { - return subtract(v2); -} - -Vector4 Vector4::operator-() { - return negate(); -} - -Vector4 Vector4::operator*(float value) { - return scale(value); -} - -Vector4 Vector4::operator*(const Vector4& v2) { - return { - x * v2.x, - y * v2.y, - z * v2.z, - w * v2.w - }; -} - -float Vector4::operator[](int index) { - switch (index) { - case 0: - return x; - case 1: - return y; - case 2: - return z; - case 3: - return w; - default: - return 0; - } -} - -void Vector4::printDebug(const char* name) { - printf("%s=Vector4(%f, %f, %f, %f)\n", name, x, y, z, w); -} - - -// *************************************** -// Mat4x4 -Mat4x4 Mat4x4::copy() { - Mat4x4 result; - memcpy(result.m, m, sizeof(float) * 16); - return result; -} - -Mat4x4 Mat4x4::scale(Vector3 v) { - Mat4x4 result = copy(); - result.m[0] = result.m[0] * v.x; - result.m[5] = result.m[5] *v.y; - result.m[10] = result.m[10] * v.z; - return result; -} - -Mat4x4 Mat4x4::translate(Vector3 v) { - Mat4x4 result = copy(); - result.m[12] += v.x; - result.m[13] += v.y; - result.m[14] += v.z; - return result; -} - -Mat4x4 Mat4x4::translateByVec2(Vector2 v) { - Mat4x4 result = copy(); - result.m[12] += v.x; - result.m[13] += v.y; - return result; -} - -Mat4x4 Mat4x4::rotate2D(float angle) { - Mat4x4 result = copy(); - result.m[0] = cos(angle); - result.m[1] = -sin(angle); - result.m[4] = sin(angle); - result.m[5] = cos(angle); - return result; -} - -Mat4x4 Mat4x4::getXRotationMatrix(float angleRadians) { - return { - { 1, 0, 0, 0, - 0, cos(angleRadians), -sin(angleRadians), 0, - 0, sin(angleRadians), cos(angleRadians), 0, - 0, 0, 0, 1 } - }; -} - -Mat4x4 Mat4x4::getYRotationMatrix(float angleRadians) { - return { - { cos(angleRadians), 0, sin(angleRadians), 0, - 0, 1, 0, 0, - -sin(angleRadians), 0, cos(angleRadians), 0, - 0, 0, 0, 1 } - }; -} - -Mat4x4 Mat4x4::getZRotationMatrix(float angleRadians) { - return { - { cos(angleRadians), -sin(angleRadians), 0, 0, - sin(angleRadians), cos(angleRadians), 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 } - }; -} - -Mat4x4 Mat4x4::rotate(float xRadians, float yRadians, float zRadians) { - Mat4x4 result = copy(); - - Mat4x4 rotationMatrix; - if (xRadians != 0) { - rotationMatrix = getXRotationMatrix(xRadians); - result = result * rotationMatrix; - } - - if (yRadians != 0) { - rotationMatrix = getYRotationMatrix(yRadians); - result = result * rotationMatrix; - } - - if (zRadians != 0) { - rotationMatrix = getZRotationMatrix(zRadians); - result = result * rotationMatrix; - } - - return result; -} - -Vector2 Mat4x4::multByVec2(Vector2 v) { - Vector4 vec4 = { v.x, v.y, 0.0, 1.0 }; - return { - vec4.x * m[0] + vec4.y * m[4] + vec4.z * m[8] + vec4.w * m[12], - vec4.x * m[1] + vec4.y * m[5] + vec4.z * m[9] + vec4.w * m[13] - }; -} - -Vector2 Mat4x4::operator*(Vector2 v) { - return multByVec2(v); -} - -Mat4x4 Mat4x4::multMat4x4(const Mat4x4& other) { - Mat4x4 result; - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - int row = i * 4; - result.m[row + j] = m[row + 0] * other.m[0 + j] + m[row + 1] * other.m[4 + j] + m[row + 2] * other.m[8 + j] + m[row + 3] * other.m[12 + j]; - } - } - - return result; -} - -Mat4x4 Mat4x4::operator*(const Mat4x4& other) { - return multMat4x4(other); -} - -Mat4x4 Mat4x4::getOrthographicMatrix(float left, float right, float bottom, float top) { - Mat4x4 result; - result.m[0] = 2.0 / (right - left); - result.m[5] = 2.0 / (top - bottom); - result.m[10] = 1.0; - result.m[12] = -(right + left) / (right - left); - result.m[13] = -(top + bottom) / (top - bottom); - return result; -} - -Mat4x4 Mat4x4::inverse() { - Mat4x4 inv; - - inv.m[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; - inv.m[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; - inv.m[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; - inv.m[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; - inv.m[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; - inv.m[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; - inv.m[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; - inv.m[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; - inv.m[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; - inv.m[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; - inv.m[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; - inv.m[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; - inv.m[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; - inv.m[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; - inv.m[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; - inv.m[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; - - float det = m[0] * inv.m[0] + m[1] * inv.m[4] + m[2] * inv.m[8] + m[3] * inv.m[12]; - - if (det == 0) - return Mat4x4(); - - det = 1.f / det; - - for (int i = 0; i < 16; i++) - inv.m[i] = inv.m[i] * det; - - return inv; -} - -Mat4x4 Mat4x4::getPerspectiveProjection(float near, float far, float fieldOfViewRadians, float aspectRatio) { - float halfFieldOfView = fieldOfViewRadians * 0.5f; - float top = tan(halfFieldOfView) * near; - float bottom = -top; - float right = top * aspectRatio; - float left = -right; - - return { - { (2 * near) / (right - left), 0, 0, 0, - 0, (2 * near) / (top - bottom), 0, 0, - (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1, - 0, 0, (-2 * far * near) / (far - near), 0 } - }; -} - -void Mat4x4::print() { - printf("[ "); - for (int idx = 0; idx < 16; idx++) { - printf("%f, ", m[idx]); - } - printf(" ]\n"); -} - -// See https://stackoverflow.com/questions/349050/calculating-a-lookat-matrix for why the dot product is in the bottom -Mat4x4 Mat4x4::getLookAt(Vector3 eye,Vector3 pointToLookAt, Vector3 up) { - Vector3 zAxis = (pointToLookAt - eye).normalize(); - Vector3 xAxis = zAxis.cross(up).normalize(); - Vector3 yAxis = xAxis.cross(zAxis).normalize(); - - return { - { xAxis.x, yAxis.x, -zAxis.x, 0, - xAxis.y, yAxis.y, -zAxis.y, 0, - xAxis.z, yAxis.z, -zAxis.z, 0, - -xAxis.dot(eye), -yAxis.dot(eye), zAxis.dot(eye), 1 } - }; -} - -// *************************************** -// Quaternion -Quaternion::Quaternion() { }; - -Quaternion::Quaternion(float inW, float inX, float inY, float inZ) { - w = inW; - x = inX; - y = inY; - z = inZ; -} - -float Quaternion::operator [](int index) { - switch (index) { - case 0: - return x; - case 1: - return y; - case 2: - return z; - case 3: - return w; - default: - return 0; - } -} - -Quaternion Quaternion::operator*(const Quaternion& other) const { - return { - w * other.w - x * other.x - y * other.y - z * other.z, // w - w * other.x + x * other.w + y * other.z - z * other.y, // i - w * other.y - x * other.z + y * other.w + z * other.x, // j - w * other.z + x * other.y - y * other.x + z * other.w // k - }; -} - -Quaternion Quaternion::operator*(const float& scale) const { - return { - w * scale, - x * scale, - y * scale, - z * scale - }; -} - -Quaternion Quaternion::operator+(const Quaternion& other) const { - return { - w + other.w, - x + other.x, - y + other.y, - z + other.z - }; -} - -Quaternion Quaternion::operator-(const Quaternion& other) const { - return { - w - other.w, - x - other.x, - y - other.y, - z - other.z - }; -} - -const float DOT_THRESHOLD = 0.9999f; - -// Using a slerp here, mostly taken from here: http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/. -// As JBlow says, it's expensive as heck. I will address this if it becomes a problem. -Quaternion Quaternion::interpolate(const Quaternion& other, const float factor) { - Quaternion newOther = other; - float dotProduct = dot(other); - - if (dotProduct < 0) { - newOther = other * -1; - dotProduct *= -1; - } - - if (dotProduct > DOT_THRESHOLD) { - return (*this + ((newOther - *this) * factor)).normalize(); - } - - float thetaZero = acos(dotProduct); // angle between input vectors - float theta = thetaZero * factor; - - Quaternion v2 = (newOther - (*this * dotProduct)).normalize(); - - return (*this * cos(theta)) + (v2 * sin(theta)); -} - -float Quaternion::length() const { - return sqrtf(x * x + y * y + z * z + w * w); -} - -Quaternion Quaternion::normalize() const { - float l = length(); - return { - w / l, - x / l, - y / l, - z / l, - }; -} - -/*Mat4x4 Quaternion::toMatrix() const { - return { - { - 1 - 2 * (y * y - z * z), - 2 * (x * y - z * w), - 2 * (x * z + w * y), - 0, - - 2 * (x * y + w * z), - 1 - 2 * (x * x - z * z), - 2 * (y * z - w * x), - 0, - - 2 * (x * z - w * y), - 2 * (y * z + w * x), - 1 - 2 * (x * x - y * y), - 0, - - 0, - 0, - 0, - 1 - } - }; -}*/ - -Mat4x4 Quaternion::toMatrix() const { - return { - { - 1 - 2 * (y * y + z * z), - 2 * (x * y + z * w), - 2 * (x * z - y * w), - 0, - 2 * (x * y - w * z), - 1 - 2 * (x * x + z * z), - 2 * (y * z + w * x), - 0, - 2 * (x * z + w * y), - 2 * (y * z - w * x), - 1 - 2 * (x * x + y * y), - 0, - 0, - 0, - 0, - 1 - } - }; -} - -float Quaternion::dot(const Quaternion& other) const { - return w * other.w + x * other.x + y * other.y + z * other.z; -} - -Quaternion quaternionFromRotation(Vector3 axis, float angleRadians) { - float halfAngleRadians = angleRadians / 2.f; - float cosHalfAngRad = cosf(halfAngleRadians); - float sinHalfAngRad = sinf(halfAngleRadians); - - return { - cosHalfAngRad, - axis.x * sinHalfAngRad, - axis.y * sinHalfAngRad, - axis.z * sinHalfAngRad - }; -} - -Quaternion quaternionFromEulerAngle(float yaw, float pitch, float roll) { - float cy = cosf(yaw * 0.5f); - float sy = sinf(yaw * 0.5f); - float cp = cosf(pitch * 0.5f); - float sp = sinf(pitch * 0.5f); - float cr = cosf(roll * 0.5f); - float sr = sinf(roll * 0.5f); - - return { - cr * cp * cy + sr * sp * sy, - sr * cp * cy - cr * sp * sy, - cr * sp * cy + sr * cp * sy, - cr * cp * sy - sr * sp * cy - }; -} - -void Quaternion::printDebug(const char* name) { - printf("%s=Quaternion(%f, %f, %f, %f)\n", name, x, y, z, w); -} diff --git a/themes/mathlib.h b/themes/mathlib.h deleted file mode 100644 index bb62f39..0000000 --- a/themes/mathlib.h +++ /dev/null @@ -1,181 +0,0 @@ -// mathlib.h -// -// Created by Matt Kosarek -// -// A file containing some common math functionality that I find -// useful in my projects. I don't like that I don't know what's happening -// in glm, so I wrote most of this myself. All mistakes are my own. -// - -#pragma once -#include -#include -#include -#include - -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) -#define ABS(x) (x < 0 ? -x : x) -#define SIGN(x) (x < 0 ? -1 : 1) -#define PI 3.141592653589793238463 -#define E 2.71828182845904523536 -#define DEG_TO_RAD(x) (x * (PI / 180.f)) -#define RAD_TO_DEG(x) (x * (180.f / PI)) - -struct Vector4; - - -// -- Random -inline float randomFloatBetween(float min, float max) { - float random = static_cast(rand()) / static_cast(RAND_MAX); - return (max - min) * random + min; -} - -inline int randomIntBetween(int min, int max) { - return static_cast(randomFloatBetween(min, max)); -} - -struct Vector2 { - float x = 0; - float y = 0; - - Vector2(); - Vector2(float inX, float inY); - Vector2 operator+(Vector2 other); - Vector2& operator+=(Vector2 other); - Vector2 operator-(Vector2 other); - Vector2 operator*(float s); - Vector2 operator/(float s); - void operator=(const Vector4& other); - float dot(Vector2 other); - float length(); - Vector2 normalize(); - Vector2 negate(); - Vector2 getPerp(); - Vector2 rotate(float angle); - Vector2 rotateAround(float angle, const Vector2& other); - float determinant(Vector2 other); - - void printDebug(const char* name); -}; - -struct Vector3 { - float x = 0.f; - float y = 0.f; - float z = 0.f; - - Vector3(); - Vector3(float value); - Vector3(float x, float y, float z); - - float length(); - float dot(const Vector3& other); - Vector3 scale(float scalar); - Vector3 add(const Vector3& other); - Vector3 subtract(const Vector3& other); - Vector3 negate(); - Vector3 cross(const Vector3& other); - Vector3 normalize(); - - Vector3 operator+(const Vector3& v2); - Vector3& operator+=(Vector3 other); - Vector3 operator-(const Vector3& v2); - Vector3 operator-(); - Vector3 operator*(float value); - Vector3 operator*(const Vector3& v2); - Vector3 operator/(const Vector3& v2); - float operator[](int index); - - void printDebug(const char* name); -}; - -struct Vector4 { - float x = 0.f; - float y = 0.f; - float z = 0.f; - float w = 1.f; - - Vector4(); - Vector4(float value); - Vector4(float inX, float inY, float inZ, float inW); - Vector4 fromColor(float r, float g, float b, float a); - Vector4 toNormalizedColor(); - Vector4(Vector2& v); - Vector4(Vector3& v); - Vector3 toVector3(); - - float length(); - Vector4 normalize(); - float dot(const Vector4& other); - Vector4 scale(float scalar); - Vector4 add(const Vector4& other); - Vector4 subtract(const Vector4& other); - Vector4 negate(); - Vector4 cross(const Vector4& other); - - void operator=(const Vector2& v2); - void operator=(const Vector3& v2); - Vector4 operator+(const Vector4& v2); - Vector4 operator-(const Vector4& v2); - Vector4 operator-(); - Vector4 operator*(float value); - Vector4 operator*(const Vector4& v2); - float operator[](int index); - - void printDebug(const char* name); -}; - -Vector4 lerp(Vector4 start, Vector4 end, float t); - -struct Mat4x4 { - float m[16] = { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - }; - - Mat4x4 copy(); - Mat4x4 scale(Vector3 v); - Mat4x4 translate(Vector3 v); - Mat4x4 translateByVec2(Vector2 v); - Mat4x4 rotate2D(float angle); - Mat4x4 getXRotationMatrix(float angleRadians); - Mat4x4 getYRotationMatrix(float angleRadians); - Mat4x4 getZRotationMatrix(float angleRadians); - Mat4x4 rotate(float xRadians, float yRadians, float zRadians); - Vector2 multByVec2(Vector2 v); - Vector2 operator*(Vector2 v); - Mat4x4 multMat4x4(const Mat4x4& other); - Mat4x4 operator*(const Mat4x4& other); - Mat4x4 getOrthographicMatrix(float left, float right, float bottom, float top); - Mat4x4 inverse(); - Mat4x4 getPerspectiveProjection(float near, float far, float fieldOfViewRadians, float aspectRatio); - Mat4x4 getLookAt(Vector3 eye,Vector3 pointToLookAt, Vector3 up); - void print(); -}; - -struct Quaternion { - float w = 1; - float x = 0; - float y = 0; - float z = 0; - - Quaternion(); - Quaternion(float inW, float inX, float inY, float inZ); - float operator [](int index); - Quaternion operator*(const Quaternion& other) const; - Quaternion operator*(const float& scale) const; - Quaternion operator+(const Quaternion& other) const; - Quaternion operator-(const Quaternion& other) const; - Quaternion interpolate(const Quaternion& other, const float factor); - Mat4x4 toMatrix() const; - Quaternion normalize() const; - float length() const; - float dot(const Quaternion& other) const; - - void printDebug(const char* name); -}; - -Quaternion quaternionFromRotation(Vector3 axis, float angleRadians); -Quaternion quaternionFromEulerAngle(float yaw, float pitch, float roll); diff --git a/themes/src/LeafParticleRender.cpp b/themes/src/LeafParticleRender.cpp new file mode 100644 index 0000000..0c6fbca --- /dev/null +++ b/themes/src/LeafParticleRender.cpp @@ -0,0 +1,166 @@ +#include "LeafParticleRender.h" +#include "Renderer2d.h" +#include "mathlib.h" +#include "TreeShape.h" +#include "types.h" +#include + +const i32 verticesPerLeaf = 6; +const f32 leafRadius = 3.f; +const i32 fallChanceMax = 100; + +inline void updateLeaf(Vertex2D* vertices, Vector2 position, Vector4 color, f32 scale) { + f32 radius = scale * leafRadius; + Vector2 bottomLeft = Vector2(-radius, -radius) + position; + Vector2 bottomRight = Vector2(radius, -radius) + position; + Vector2 topLeft = Vector2(-radius, radius) + position; + Vector2 topRight = Vector2(radius, radius) + position; + + vertices[0] = { bottomLeft, color, Mat4x4() }; + vertices[1] = { bottomRight, color, Mat4x4() }; + vertices[2] = { topLeft, color, Mat4x4() }; + vertices[3] = { topLeft, color, Mat4x4() }; + vertices[4] = { topRight, color, Mat4x4() }; + vertices[5] = { bottomRight, color, Mat4x4() }; +} + +void LeafParticleRender::load(Renderer2d *renderer, TreeShapeLoadResult* lr) { + LeafParticleLoadData ld; + ld.numLeaves = 256; + numLeaves = ld.numLeaves; + numVertices = ld.numLeaves * verticesPerLeaf; + + updateData = new LeafParticleUpdateData[numLeaves]; + vertices = new Vertex2D[numVertices]; + + for (i32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) { + i32 randomBranch = randomIntBetween(0, lr->numBranches); + i32 randomVertex = randomIntBetween(0, 6); // TODO: Manually entering num vertices per branch. + updateData[leafIdx].vertexToFollow = &lr->updateData[randomBranch].vertices[randomVertex]; + updateData[leafIdx].fallChance = randomIntBetween(0, fallChanceMax); + updateData[leafIdx].color = Vector4(randomFloatBetween(0.3, 0.9), randomFloatBetween(0.1, 0.6), 0, 1); + updateData[leafIdx].vertexPtr = &vertices[leafIdx * verticesPerLeaf]; + updateData[leafIdx].resetTime = randomFloatBetween(4.f, 6.f); + } + + useShader(renderer->shader); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Vertex2D), &vertices[0], GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(renderer->attributes.position); + glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); + + glEnableVertexAttribArray(renderer->attributes.color); + glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); + + for (i32 idx = 0; idx < 4; idx++) { + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16))); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void LeafParticleRender::update(f32 dtSeconds) { + elapsedTimeSeconds += dtSeconds; + + // Every time the fallIntervalSeconds passes, we remove one leaf + // from the tree and send it barrelling towards the earth. + i32 fallRoll; + bool didGenerateFall = false; + if (elapsedTimeSeconds >= fallIntervalSeconds) { + fallRoll = randomIntBetween(0, fallChanceMax); + didGenerateFall = true; + elapsedTimeSeconds = 0; + } + + for (i32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) { + auto updateDataItem = &updateData[leafIdx]; + + if (didGenerateFall) { + if (updateDataItem->state == LeafParticleState::OnTree && updateDataItem->fallChance == fallRoll) { + updateDataItem->state = LeafParticleState::Falling; + updateDataItem->fallPosition = updateDataItem->vertexToFollow->position; + updateDataItem->fallVerticalVelocity = -randomFloatBetween(15.f, 25.f); + updateDataItem->fallHorizontalFrequency = randomFloatBetween(3.f, 5.f); + } + } + + switch (updateDataItem->state) { + case (LeafParticleState::Remerging): { + updateDataItem->timeElapsedSeconds += dtSeconds; + + if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) { + updateDataItem->timeElapsedSeconds = 0.f; + updateDataItem->state = LeafParticleState::OnTree; + updateDataItem->color.w = 1.f; + updateDataItem->scale = 1.f; + } + else { + updateDataItem->color.w = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime); + updateDataItem->scale = (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime); + } + + updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale); + break; + } + case (LeafParticleState::OnGround): { + updateDataItem->timeElapsedSeconds += dtSeconds; + + if (updateDataItem->timeElapsedSeconds >= updateDataItem->resetTime) { + updateDataItem->timeElapsedSeconds = 0.f; + updateDataItem->color.w = 0.f; + updateDataItem->state = LeafParticleState::Remerging; + } + else { + updateDataItem->color.w = 1.f - (updateDataItem->timeElapsedSeconds / updateDataItem->resetTime); + updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale); + } + break; + } + case (LeafParticleState::Falling): { + updateDataItem->timeElapsedSeconds += dtSeconds; + const f32 xPosUpdate = cosf(updateDataItem->fallHorizontalFrequency * updateDataItem->timeElapsedSeconds); + updateDataItem->fallPosition.x += xPosUpdate; + updateDataItem->fallPosition.y += updateDataItem->fallVerticalVelocity * dtSeconds; + if (updateDataItem->fallPosition.y <= 50.f) { // TODO: Hardcoded ground for now + updateDataItem->fallPosition.y = 50.f; + updateDataItem->state = LeafParticleState::OnGround; + updateDataItem->timeElapsedSeconds = 0; + updateDataItem->resetTime = randomFloatBetween(2.f, 5.f); // TODO: Hardcoded reset interval + } + updateLeaf(updateDataItem->vertexPtr, updateDataItem->fallPosition, updateDataItem->color, updateDataItem->scale); + break; + } + case (LeafParticleState::OnTree): { + updateLeaf(updateDataItem->vertexPtr, updateDataItem->vertexToFollow->position, updateDataItem->color, updateDataItem->scale); + break; + } + } + } +} + +void LeafParticleRender::render(Renderer2d *renderer) { + setShaderMat4(renderer->uniforms.model, model); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2D), &vertices[0]); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, numVertices); + glBindVertexArray(0); +} + +void LeafParticleRender::unload() { + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vbo); + delete [] vertices; + + elapsedTimeSeconds = 0; +} diff --git a/themes/src/LeafParticleRender.h b/themes/src/LeafParticleRender.h new file mode 100644 index 0000000..713d9f6 --- /dev/null +++ b/themes/src/LeafParticleRender.h @@ -0,0 +1,58 @@ +#include "Renderer2d.h" +#include "mathlib.h" +#include "types.h" + +struct TreeShapeLoadResult; + +struct LeafParticleLoadData { + Vector2 initPosition; + Vector4 initColor; + int numLeaves = 48; +}; + +enum LeafParticleState { + OnTree, + Falling, + OnGround, + Remerging +}; + +struct LeafParticleUpdateData { + LeafParticleState state = LeafParticleState::Remerging; + + Vertex2D* vertexToFollow = NULL; + Vector4 color = Vector4(1.f, 0.f, 0.f, 0.f); + f32 scale = 1.f; + + f32 timeElapsedSeconds = 0.f; + i32 fallChance = -1; + Vector2 fallPosition; + f32 fallVerticalVelocity; + f32 fallHorizontalFrequency; + + f32 resetTime = 0.f; + + Vertex2D* vertexPtr = NULL; +}; + +struct LeafParticleRender { + f32 elapsedTimeSeconds = 0.5; + f32 fallIntervalSeconds = 1.f; + + // Update data + i32 numLeaves = 0; + + LeafParticleUpdateData* updateData = NULL; + Vertex2D* vertices = NULL; + + // Render data + u32 vao; + u32 vbo; + u32 numVertices = 0; + Mat4x4 model; + + void load(Renderer2d* renderer, TreeShapeLoadResult* lr); + void update(f32 dtSeconds); + void render(Renderer2d* renderer); + void unload(); +}; \ No newline at end of file diff --git a/themes/src/Logger.cpp b/themes/src/Logger.cpp new file mode 100644 index 0000000..1068d88 --- /dev/null +++ b/themes/src/Logger.cpp @@ -0,0 +1,123 @@ +#include "Logger.h" +#include +#include +#include + +namespace Logger { + LogLevel gLogLevel = LogLevel_Debug; + FILE* gFilePointer = NULL; + + void initialize(LoggerOptions options) { + setLevel(options.level); + if (options.logToFile) { +#ifdef WIN32 + fopen_s(&gFilePointer, options.filePath, "a"); +#else + gFilePointer = fopen(options.filePath, "a"); +#endif + } + } + + void setLevel(LogLevel level) { + gLogLevel = level; + } + + LogLevel getLevel() { + return gLogLevel; + } + + void printHeader(const char* levelStr, const char* fileName, int lineNumber) { + time_t t = time(0); + tm now; +#ifdef WIN32 + localtime_s(&now, &t); +#else + now = *localtime(&t); +#endif + + printf("%s:%d [%d-%d-%d %d:%d:%d] %s: ", fileName, lineNumber, (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); + if (gFilePointer != NULL) { + fprintf(gFilePointer, "[%d-%d-%d %d:%d:%d] %s: ", (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); + } + } + + void logInternal(const char* file, int lineNumber,LogLevel level, const char* format, va_list args) { + if (level < gLogLevel) { + return; + } + + + const char* levelStr; + switch (level) { + case LogLevel_Debug: + levelStr = "Debug"; + break; + case LogLevel_Info: + levelStr = "Info"; + break; + case LogLevel_Warn: + levelStr = "Warning"; + break; + case LogLevel_Error: + levelStr = "Error"; + break; + default: + levelStr = "Unknown"; + break; + } + + if (gFilePointer != NULL) { + va_list fileArgs; + va_copy(fileArgs, args); + vfprintf(gFilePointer, format, fileArgs); + fprintf(gFilePointer, "\n"); + } + + printHeader(levelStr, file, lineNumber); + + vprintf(format, args); + printf("\n"); + } + + void doLog(const char* file, int lineNumber,LogLevel level, const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, level, format, args); + va_end(args); + } + + void doDebug(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Debug, format, args); + va_end(args); + } + + void doInfo(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Info, format, args); + va_end(args); + } + + void doWarning(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Warn, format, args); + va_end(args); + } + + void doError(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Error, format, args); + va_end(args); + } + + void free() { + if (gFilePointer) { + fclose(gFilePointer); + gFilePointer = NULL; + } + } +} diff --git a/themes/src/Logger.h b/themes/src/Logger.h new file mode 100644 index 0000000..7596b6f --- /dev/null +++ b/themes/src/Logger.h @@ -0,0 +1,43 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include + +enum LogLevel { + LogLevel_Debug = 0, + LogLevel_Info = 1, + LogLevel_Warn = 2, + LogLevel_Error = 3 +}; + +struct LoggerOptions { + LogLevel level = LogLevel_Debug; + bool logToFile = false; + const char* filePath = "debug.log"; +}; + +namespace Logger { + void initialize(LoggerOptions options); + void setLevel(LogLevel level); + LogLevel getLevel(); + void doLog(const char* file, int lineNumber, LogLevel level, const char* format, ...); + void doDebug(const char* file, int lineNumber, const char* format, ...); + void doInfo(const char* file, int lineNumber, const char* format, ...); + void doWarning(const char* file, int lineNumber, const char* format, ...); + void doError(const char* file, int lineNumber, const char* format, ...); + void free(); +}; + +#if WIN32 +#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#else +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +#define logger_log(level, format, ...) Logger::doLog(__FILENAME__, __LINE__, level, format, ## __VA_ARGS__) +#define logger_debug(format, ...) Logger::doDebug(__FILENAME__, __LINE__, format, ## __VA_ARGS__) +#define logger_info(format, ...) Logger::doInfo(__FILENAME__, __LINE__, format, ## __VA_ARGS__) +#define logger_warning(format, ...) Logger::doWarning(__FILENAME__, __LINE__, format, ## __VA_ARGS__) +#define logger_error(format, ...) Logger::doError(__FILENAME__, __LINE__, format, ## __VA_ARGS__) + +#endif diff --git a/themes/src/MainLoop.cpp b/themes/src/MainLoop.cpp new file mode 100644 index 0000000..09aa643 --- /dev/null +++ b/themes/src/MainLoop.cpp @@ -0,0 +1,31 @@ +#include "MainLoop.h" +#include +#include + +EM_BOOL loop(double time, void* loop) { + MainLoop* mainLoop = (MainLoop*) loop; + if (!mainLoop->isRunning) { + return false; + } + + if (mainLoop->lastTime == 0) { + mainLoop->lastTime = time; + return true; + } + + long deltaTime = time - mainLoop->lastTime; + mainLoop->lastTime = time; + mainLoop->elapsedTime += deltaTime; + mainLoop->numFrames++; + float deltaTimeSeconds = static_cast(deltaTime) / 1000.f; + + if (mainLoop->elapsedTime >= 1000.0) { + printf("FPS: %d\n", mainLoop->numFrames); + + mainLoop->elapsedTime = 0.0; + mainLoop->numFrames = 0; + } + + mainLoop->updateFunc(deltaTimeSeconds, NULL); + return true; +} \ No newline at end of file diff --git a/themes/src/MainLoop.h b/themes/src/MainLoop.h new file mode 100644 index 0000000..2573bb8 --- /dev/null +++ b/themes/src/MainLoop.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include + +EM_BOOL loop(double time, void* loop); + +struct MainLoop { + bool isRunning = false; + double lastTime = 0, elapsedTime = 0; + int numFrames = 0; + void (*updateFunc)(float dtSeconds, void *userData); + + void run(void (*cb)(float dtSeconds, void *userData)) { + isRunning = true; + lastTime = 0; + elapsedTime = 0; + numFrames = 0; + updateFunc = cb; + + emscripten_request_animation_frame_loop(loop, this); + } + + void stop() { + isRunning = false; + } +}; \ No newline at end of file diff --git a/themes/src/Renderer2d.cpp b/themes/src/Renderer2d.cpp new file mode 100644 index 0000000..c303e83 --- /dev/null +++ b/themes/src/Renderer2d.cpp @@ -0,0 +1,132 @@ +#include "Renderer2d.h" +#include "Shader.h" +#include "WebglContext.h" +#include "mathlib.h" +#include + +// Note: In the 'transform' attribute, the transform.x is the scale, +// transform.y is the rotation, and transform.zw is the translation. +const char* Vertex2DShader = +"attribute vec2 position; \n" +"attribute vec4 color; \n" +"attribute mat4 vMatrix; \n" +"uniform mat4 projection; \n" +"uniform mat4 model; \n" +"varying lowp vec4 VertexColor; \n" +"void main() { \n" +" vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); \n" +" gl_Position = fragmentPosition; \n" +" VertexColor = color; \n" +"}"; + +const char* renderer2dFragmentShader = +"varying lowp vec4 VertexColor; \n" +"void main() { \n" +" gl_FragColor = VertexColor; \n" +"}"; + +void Renderer2d::load(WebglContext* inContext) { + context = inContext; + printf("Compiling Renderer2d shader...\n"); + shader = loadShader(Vertex2DShader, renderer2dFragmentShader); + + useShader(shader); + attributes.position = getShaderAttribute(shader, "position"); + attributes.color = getShaderAttribute(shader, "color"); + attributes.vMatrix = getShaderAttribute(shader, "vMatrix"); + uniforms.projection = getShaderUniform(shader, "projection"); + uniforms.model = getShaderUniform(shader, "model"); + projection = Mat4x4().getOrthographicMatrix(0, context->width, 0, context->height); + + printf("Renderer2d shader compiled.\n"); +} + +void Renderer2d::render() { + projection = Mat4x4().getOrthographicMatrix(0, context->width, 0, context->height); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + useShader(shader); + setShaderMat4(uniforms.projection, projection); +} + +void Renderer2d::unload() { + glClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + glDeleteProgram(shader); +} + + +void Mesh2D::load(Vertex2D* inVertices, u32 inNumVertices, Renderer2d* renderer) { + ebo = 0; + numVertices = inNumVertices; + useShader(renderer->shader); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, inNumVertices * sizeof(Vertex2D), &inVertices[0], GL_STATIC_DRAW); + + glEnableVertexAttribArray(renderer->attributes.position); + glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); + + glEnableVertexAttribArray(renderer->attributes.color); + glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); + + for (i32 idx = 0; idx < 4; idx++) { + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16))); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + + +void Mesh2D::load(Vertex2D* vertices, + u32 numVertices, + u32* indices, + u32 inNumIndices, + Renderer2d* renderer) { + load(vertices, numVertices, renderer); + glBindVertexArray(vao); + glGenBuffers(1, &ebo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, inNumIndices * sizeof(u32), &indices[0], GL_STATIC_DRAW); + numIndices = inNumIndices; + glBindVertexArray(0); +} + +void Mesh2D::render(Renderer2d* renderer, GLenum drawType) { + setShaderMat4(renderer->uniforms.model, model); + + if (ebo == 0) { + glBindVertexArray(vao); + glDrawArrays(drawType, 0, numVertices); + glBindVertexArray(0); + } + else { + glBindVertexArray(vao); + glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + } +} + +void Mesh2D::unload() { + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vbo); + if (ebo != 0) { + glDeleteBuffers(1, &ebo); + ebo = 0; + } + vao = 0; + vbo = 0; +} diff --git a/themes/src/Renderer2d.h b/themes/src/Renderer2d.h new file mode 100644 index 0000000..909f088 --- /dev/null +++ b/themes/src/Renderer2d.h @@ -0,0 +1,54 @@ +#pragma once + +#include "WebglContext.h" +#include "types.h" +#include "Shader.h" +#include "mathlib.h" + +struct WebglContext; + +struct Renderer2d { + WebglContext* context = NULL; + Mat4x4 projection; + u32 shader; + Vector4 clearColor; + + struct { + i32 position; + i32 color; + i32 vMatrix; + } attributes; + + struct { + i32 projection; + i32 model; + } uniforms; + + void load(WebglContext* context); + void render(); + void unload(); +}; + +struct Vertex2D { + Vector2 position; + Vector4 color; + Mat4x4 vMatrix; +}; + +struct Mesh2D { + u32 vao; + u32 vbo; + u32 ebo = 0; + u32 numVertices = 0; + u32 numIndices = 0; + Mat4x4 model; + + void load(Vertex2D* vertices, u32 numVertices, Renderer2d* renderer); + void load(Vertex2D* vertices, + u32 numVertices, + u32* indices, + u32 numIndices, + Renderer2d* renderer); + void render(Renderer2d* renderer, GLenum drawType = GL_TRIANGLES); + void unload(); +}; diff --git a/themes/src/Renderer3d.cpp b/themes/src/Renderer3d.cpp new file mode 100644 index 0000000..5f9ce88 --- /dev/null +++ b/themes/src/Renderer3d.cpp @@ -0,0 +1,263 @@ +#include "Renderer3d.h" +#include "Shader.h" +#include "list.h" +#include "mathlib.h" +#include "WebglContext.h" +#include "Logger.h" +#include + +// Note: In the 'transform' attribute, the transform.x is the scale, +// transform.y is the rotation, and transform.zw is the translatiob. +const char* vertexShader = + "attribute vec4 position; \n" + "attribute vec4 color; \n" + "attribute vec4 normal; \n" + "uniform mat4 projection; \n" + "uniform mat4 view; \n" + "uniform mat4 model; \n" + "varying lowp vec4 VertexColor; \n" + "varying lowp vec4 VertexNormal; \n" + "void main() { \n" + " vec4 fragmentPosition = projection * view * model * position; \n" + " gl_Position = fragmentPosition; \n" + " VertexColor = color; \n" + " VertexNormal = normal; \n" + "}"; + +const char* fragmentShader = + "varying lowp vec4 VertexColor; \n" + "varying lowp vec4 VertexNormal; \n" + "void main() { \n" + " const lowp vec3 lightDirection = vec3(0.0, 1.0, 0.0);\n" + " gl_FragColor = vec4(VertexColor.xyz * dot(VertexNormal.xyz, lightDirection), 1); \n" + "}"; + +EM_BOOL onScreenSizeChanged_3D(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) { + Renderer3D* renderer = (Renderer3D*)userData; + + EMSCRIPTEN_RESULT result = emscripten_set_canvas_element_size( renderer->context->query, uiEvent->documentBodyClientWidth, uiEvent->documentBodyClientHeight); + if (result != EMSCRIPTEN_RESULT_SUCCESS) { + logger_error("Failed to resize element at query: %s\n", renderer->context->query); + } + //renderer->projection = Mat4x4().getOrthographicMatrix(0, renderer->context->width, 0, renderer->context->height); + + return true; +} + +void Renderer3D::load(WebglContext* inContext) { + context = inContext; + printf("Compiling Renderer2d shader...\n"); + shader = loadShader(vertexShader, fragmentShader); + + useShader(shader); + attributes.position = getShaderAttribute(shader, "position"); + attributes.color = getShaderAttribute(shader, "color"); + attributes.normal = getShaderAttribute(shader, "normal"); + uniforms.projection = getShaderUniform(shader, "projection"); + uniforms.view = getShaderUniform(shader, "view"); + uniforms.model = getShaderUniform(shader, "model"); + projection = Mat4x4().getPerspectiveProjection(0.1, 1000.f, 0.872f, static_cast(context->width) / static_cast(context->height)); + view = Mat4x4().getLookAt({ 0, 25, 75 }, { 0, 15, 0 }, { 0, 1, 0 }); + + logger_info("Renderer2d shader compiled.\n"); + + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, onScreenSizeChanged_3D); +} + +void Renderer3D::render() { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + useShader(shader); + setShaderMat4(uniforms.projection, projection); + setShaderMat4(uniforms.view, view); +} + +void Renderer3D::unload() { + glClearColor(0.f, 0.f, 0.f, 0.f); + glClear(GL_COLOR_BUFFER_BIT); + glDeleteProgram(shader); +} + +enum LineType { + LineType_None, + LineType_Comment, + LineType_mtl, + LineType_v, + LineType_f, + LineType_Unsupported +}; + +struct LineItem { + LineType type = LineType_None; + + i32 idx = 0; + + union { + f32 vertices[3]; + i32 indices[3]; + } v; +}; + +inline i32 readPastSpaces(i32 i, const char* content) { + while (content[i] == ' ') i++; + return i; +} + +inline i32 readPastLine(i32 i, const char* content) { + while (content[i] != '\n' && content[i] != '\0') i++; + return i; +} + +inline i32 readToken(i32 i, const char* content, char* output) { + i32 tidx = 0; + i = readPastSpaces(i, content); + while (content[i] != ' ' && content[i] != '\n' && content[i] != '\0') { + output[tidx] = content[i]; + i++; + tidx++; + } + output[tidx] = '\0'; + return i; +} + +Mesh3d Mesh3d_fromObj(Renderer3D* renderer, const char* content, const i32 len) { + Mesh3d result; + result.vertices.allocate(2048); + result.indices.allocate(2048); + + LineItem lt; + lt.type = LineType_None; + i32 lineNumber = 0; + i32 i = 0; + while (content[i] != '\0') { + i = readPastSpaces(i, content); + if (lt.type == LineType_None) { + lineNumber++; + char type[32]; + i = readToken(i, content, type); + + if (strncmp(type, "#", 1) == 0) { + lt.type = LineType_Comment; + } + else if (strncmp(type, "mtllib", 6) == 0) { + lt.type = LineType_mtl; + } + else if (strncmp(type, "v", 1) == 0) { + lt.type = LineType_v; + } + else if (strncmp(type, "f", 1) == 0) { + lt.type = LineType_f; + } + else { + i++; + //lt.type = LineType_Unsupported; + //logger_error("Unknown type %s, %d", type, lineNumber); + } + } + else { + char buffer[32]; + switch (lt.type) { + case LineType_mtl: + i = readToken(i, content, buffer); + break; + case LineType_v: { + while (content[i] != '\n' && content[i] != '\0') { + i = readToken(i, content, buffer); + lt.v.vertices[lt.idx] = atof(buffer); + lt.idx++; + } + + float fColor = randomFloatBetween(0.8, 1); + result.vertices.add({ + Vector4(lt.v.vertices[0], lt.v.vertices[1], lt.v.vertices[2], 1.f), + Vector4(fColor, fColor, fColor, 1) + }); + break; + } + case LineType_f: { + while (content[i] != '\n' && content[i] != '\0') { + i = readToken(i, content, buffer); + lt.v.indices[lt.idx] = atoi(buffer); + lt.idx++; + } + + auto v1idx = lt.v.indices[0] - 1; + auto v2idx = lt.v.indices[1] - 1; + auto v3idx = lt.v.indices[2] - 1; + + result.indices.add(v1idx); + result.indices.add(v2idx); + result.indices.add(v3idx); + + auto& v1 = result.vertices[v1idx]; + auto& v2 = result.vertices[v2idx]; + auto& v3 = result.vertices[v3idx]; + Vector3 normal = (v1.position - v2.position).cross(v1.position - v3.position).toVector3().normalize(); + v1.normal = normal; + v2.normal = normal; + v3.normal = normal; + break; + } + default: + i = readPastLine(i, content); + break; + } + + lt = LineItem(); + } + } + + printf("Completed Mesh3d loading.\n"); + result.load(renderer); + return result; +} + +void Mesh3d::load(Renderer3D* renderer) { + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glGenBuffers(1, &ebo); + + glBindVertexArray(vao); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, vertices.numElements * sizeof(Vertex3d), &vertices.data[0], GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.numElements * sizeof(GLuint), &indices.data[0], GL_STATIC_DRAW); + + // Position + glEnableVertexAttribArray(renderer->attributes.position); + glVertexAttribPointer(renderer->attributes.position, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex3d), (GLvoid *)0); + + // Color + glEnableVertexAttribArray(renderer->attributes.color); + glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex3d), (GLvoid *)offsetof(Vertex3d, color)); + + // Normal + glEnableVertexAttribArray(renderer->attributes.normal); + glVertexAttribPointer(renderer->attributes.normal, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex3d), (GLvoid *)offsetof(Vertex3d, normal)); + + glBindVertexArray(0); +} + +void Mesh3d::unload() { + if (vao) glDeleteVertexArrays(1, &vao); + if (vbo) glDeleteBuffers(1, &vbo); + if (ebo) glDeleteBuffers(1, &ebo); + vertices.deallocate(); + indices.deallocate(); +} + +void Mesh3d::render(Renderer3D* renderer) { + setShaderMat4(renderer->uniforms.model, model); + + glBindVertexArray(vao); + glDrawElements(GL_TRIANGLES, indices.numElements, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); +} diff --git a/themes/src/Renderer3d.h b/themes/src/Renderer3d.h new file mode 100644 index 0000000..7e89c93 --- /dev/null +++ b/themes/src/Renderer3d.h @@ -0,0 +1,56 @@ +#ifndef RENDERER3D_H +#define RENDERER3D_H +#include "mathlib.h" +#include "list.h" +#include "types.h" +#include + +struct Renderer3D; + +struct Vertex3d { + Vector4 position; + Vector4 color; + Vector4 normal; +}; + +struct Mesh3d { + u32 vao; + u32 vbo; + u32 ebo; + matte::List vertices; + matte::List indices; + Mat4x4 model; + + void load(Renderer3D* renderer); + void render(Renderer3D* renderer); + void unload(); +}; + +struct WebglContext; +struct Renderer3D { + WebglContext* context = NULL; + Mat4x4 projection; + Mat4x4 view; + u32 shader; + Vector4 clearColor; + + struct { + i32 position; + i32 color; + i32 normal; + } attributes; + + struct { + i32 projection; + i32 view; + i32 model; + } uniforms; + + void load(WebglContext* context); + void render(); + void unload(); +}; + +Mesh3d Mesh3d_fromObj(Renderer3D* renderer, const char* content, const i32 len); + +#endif diff --git a/themes/src/Shader.cpp b/themes/src/Shader.cpp new file mode 100644 index 0000000..5f2b00e --- /dev/null +++ b/themes/src/Shader.cpp @@ -0,0 +1,61 @@ +#include "Shader.h" +#include + +GLuint loadIndividualShader(GLenum shaderType, const GLchar* cCode) { + GLuint shader = glCreateShader(shaderType); + glShaderSource(shader, 1, &cCode, 0); + glCompileShader(shader); + GLint success; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLchar infoLog[512]; + glGetShaderInfoLog(shader, 512, 0, infoLog); + printf("Failed to load shader: %s, Shader =%s\n", infoLog, cCode); + return 0; + } + + return shader; +} + +void attachShaders(Shader& retVal, const GLchar* vertexShader, const GLchar* fragmentShader) { + GLuint vertex = 0, fragment = 0, geometry = 0; + if (vertexShader) { + vertex = loadIndividualShader(GL_VERTEX_SHADER, vertexShader); + glAttachShader(retVal, vertex); + } + + if (fragmentShader) { + fragment = loadIndividualShader(GL_FRAGMENT_SHADER, fragmentShader); + glAttachShader(retVal, fragment); + } + + glLinkProgram(retVal); + GLint isLinked = 0; + glGetProgramiv(retVal, GL_LINK_STATUS, (int*)&isLinked); + if (isLinked == GL_FALSE) { + GLint maxLength = 0; + glGetProgramiv(retVal, GL_INFO_LOG_LENGTH, &maxLength); + + // The maxLength includes the NULL character + GLchar* infoLog = new GLchar[maxLength]; + glGetProgramInfoLog(retVal, maxLength, &maxLength, infoLog); + glDeleteProgram(retVal); + printf("Error. Could not initialize shader with vertex=%s, error=%s\n", vertexShader, infoLog); + delete []infoLog; + } + + if (vertexShader) + glDeleteShader(vertex); + if (fragmentShader) + glDeleteShader(fragment); +} + +Shader loadShader(const GLchar* vertexShader, const GLchar* fragmentShader) { + Shader retVal; + retVal = glCreateProgram(); + + attachShaders(retVal, vertexShader, fragmentShader); + useShader(retVal); + + return retVal; +} diff --git a/themes/src/Shader.h b/themes/src/Shader.h new file mode 100644 index 0000000..bc81764 --- /dev/null +++ b/themes/src/Shader.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include +#include +#include "mathlib.h" + +typedef GLuint Shader; + +Shader loadShader(const GLchar* vertexShader, const GLchar* fragmentShader); + +inline GLint getShaderUniform(const Shader& shader, const GLchar *name) { + GLint uid = glGetUniformLocation(shader, name); + if (uid < 0) { + return -1; + } + return uid; +} + +inline GLint getShaderAttribute(const Shader& shader, const GLchar *name) { + printf("Getting attribute for shader, name: %d, %s\n", shader, name); + GLint uid = glGetAttribLocation(shader, name); + if (uid < 0) { + printf("Unable to get attribute %s for shader %d\n", name, shader); + return -1; + } + return uid; +} + +inline void useShader(const Shader& shader) { + glUseProgram(shader); +} + +inline void setShaderFloat(GLint location, GLfloat value) { + glUniform1f(location, value); +} + +inline void setShaderInt(GLint location, GLint value) { + glUniform1i(location, value); +} + +inline void setShaderUint(GLint location, GLuint value) { + glUniform1ui(location, value); +} + +inline void setShaderVec2(GLint location, const Vector2& value) { + glUniform2f(location, value.x, value.y); +} + +inline void setShaderMat4(GLint location, const Mat4x4& matrix) { + glUniformMatrix4fv(location, 1, GL_FALSE, matrix.m); +} + +inline void setShaderBVec3(GLint location, bool first, bool second, bool third) { + glUniform3i(location, first, second, third); +} + +inline void setShaderBVec4(GLint location, bool first, bool second, bool third, bool fourth) { + glUniform4i(location, first, second, third, fourth); +} + +inline void setShaderBool(GLint location, bool value) { + glUniform1i(location, value); +} diff --git a/themes/src/Snowflake.cpp b/themes/src/Snowflake.cpp new file mode 100644 index 0000000..7cab8b3 --- /dev/null +++ b/themes/src/Snowflake.cpp @@ -0,0 +1,135 @@ +#include "Snowflake.h" +#include "Renderer2d.h" +#include "mathlib.h" +#include "list.h" +#include + +const Vector4 snowColor = Vector4(1.0, 0.98, 0.98, 1); + +inline void generateSnowflakeShape(matte::List* vertices, i32 numArms, f32 radius, f32 innerRadius) { + f32 dx = ((2 * PI) / numArms) / 3.0; + for (i32 centerIdx = 0; centerIdx < (3 * numArms); centerIdx+=3) { + f32 degreeStart = dx * centerIdx; + f32 degreeEnd = dx * (centerIdx + 1); + + f32 cosStart = cosf(degreeStart); + f32 cosEnd = cosf(degreeEnd); + + f32 sinStart = sinf(degreeStart); + f32 sinEnd = sinf(degreeEnd); + + Vector2 leftEnd = Vector2(radius * cosStart, radius * sinStart); + Vector2 rightEnd = Vector2(radius * cosEnd, radius * sinEnd); + Vector2 diff = (rightEnd - leftEnd) / 2.0; + Vector2 leftStart = Vector2(-diff.x, -diff.y) + Vector2(innerRadius * cosStart, innerRadius * sinStart); + Vector2 rightStart = diff + Vector2(innerRadius * cosEnd, innerRadius * sinEnd); + + vertices->add({ leftStart, snowColor, Mat4x4() }); + vertices->add({ leftEnd, snowColor, Mat4x4() }); + vertices->add({ rightEnd, snowColor, Mat4x4() }); + vertices->add({ rightEnd, snowColor, Mat4x4() }); + vertices->add({ rightStart, snowColor, Mat4x4() }); + vertices->add({ leftStart, snowColor, Mat4x4() }); + } +} + +inline void initFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud) { + ud->vtxIdx = renderer->vertices.numElements; + generateSnowflakeShape(&renderer->vertices, randomIntBetween(4, 16), randomFloatBetween(8.f, 16.f), randomFloatBetween(2.f, 6.f)); + ud->numVertices = renderer->vertices.numElements - ud->vtxIdx; + ud->velocity = Vector2(randomFloatBetween(-10, 10), randomFloatBetween(-100, -85)); + ud->position = Vector2(randomFloatBetween(0, renderer->xMax), randomFloatBetween(renderer->yMax, -renderer->yMax)); +} + +void SnowflakeParticleRenderer::load(SnowflakeLoadParameters params, Renderer2d* renderer) { + numSnowflakes = params.numSnowflakes; + + updateData = new SnowflakeUpdateData[params.numSnowflakes]; + + xMax = static_cast(renderer->context->width); + yMax = static_cast(renderer->context->height); + + vertices.deallocate(); + vertices.growDynamically = true; + + // Initialize each snow flake with its shape + for (i32 s = 0; s < numSnowflakes; s++) { + auto ud = &updateData[s]; + initFlake(this, ud); + } + + useShader(renderer->shader); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, vertices.numElements * sizeof(Vertex2D), &vertices.data[0], GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(renderer->attributes.position); + glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); + + glEnableVertexAttribArray(renderer->attributes.color); + glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); + + for (i32 idx = 0; idx < 4; idx++) { + i32 offset = (4 * sizeof(f32)) * idx; + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, + 4, + GL_FLOAT, + GL_FALSE, + sizeof(Vertex2D), + (GLvoid *)(offsetof(Vertex2D, vMatrix) + offset)); + //glVertexAttribDivisor(renderer->attributes.vMatrix + idx, 1); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +inline void updateFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud, i32 s, f32 dtSeconds, bool addWind) { + if (addWind) ud->velocity += renderer->windSpeed; + ud->position += ud->velocity * dtSeconds; + + Mat4x4 m = Mat4x4().translateByVec2(ud->position); + for (i32 v = ud->vtxIdx; v < (ud->vtxIdx + ud->numVertices); v++) { + renderer->vertices.data[v].vMatrix = m; + } +} + +void SnowflakeParticleRenderer::update(f32 dtSeconds) { + bool addWind = false; + timeUntilNextWindSeconds -= dtSeconds; + if (timeUntilNextWindSeconds < 0) { + timeUntilNextWindSeconds = windIntervalSeconds; + windSpeed = Vector2(randomFloatBetween(-10, 10), randomFloatBetween(-10, 0)); + addWind = true; + } + + for (i32 s = 0; s < numSnowflakes; s++) { + SnowflakeUpdateData* ud = &updateData[s]; + updateFlake(this, ud, s, dtSeconds, addWind); + } +} + +void SnowflakeParticleRenderer::render(Renderer2d* renderer) { + setShaderMat4(renderer->uniforms.model, model); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.numElements * sizeof(Vertex2D), &vertices.data[0]); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, vertices.numElements); + glBindVertexArray(0); +} + +void SnowflakeParticleRenderer::unload() { + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vbo); + vao = 0; + vbo = 0; + vertices.deallocate(); + delete [] updateData; +} diff --git a/themes/src/Snowflake.h b/themes/src/Snowflake.h new file mode 100644 index 0000000..c6323d6 --- /dev/null +++ b/themes/src/Snowflake.h @@ -0,0 +1,47 @@ +#include "types.h" +#include "mathlib.h" +#include "list.h" + +struct Renderer2d; +struct Vertex2D; + +struct SnowflakeLoadParameters { + i32 numSnowflakes = 1000; + f32 rateOfSnowfall = 0.1f; + Vector2 flakeV0 = { 0, 1 }; + f32 flakeSize = 5.f; + f32 flakeSizeDeviation = 1.f; + Vector4 snowColor = { 0.8, 0.8, 0.8, 1.0 }; + f32 windIntervalSeconds = 1.5; +}; + +struct SnowflakeUpdateData { + Vector2 v0; + Vector2 velocity; + Vector2 position; + f32 rotation; + bool onGround = false; + + i32 vtxIdx = 0; + i32 numVertices = 0; +}; + +struct SnowflakeParticleRenderer { + f32 xMax = 0; + f32 yMax = 0; + f32 windIntervalSeconds = 1.5; + i32 numSnowflakes = 0; + Vector2 windSpeed; + SnowflakeUpdateData* updateData; + f32 timeUntilNextWindSeconds = 0; + + u32 vao; + u32 vbo; + Mat4x4 model; + matte::List vertices; + + void load(SnowflakeLoadParameters params, Renderer2d* renderer); + void update(f32 dtSeconds); + void render(Renderer2d* renderer); + void unload(); +}; diff --git a/themes/src/SummerTheme.cpp b/themes/src/SummerTheme.cpp new file mode 100644 index 0000000..20bb310 --- /dev/null +++ b/themes/src/SummerTheme.cpp @@ -0,0 +1,67 @@ +#include "SummerTheme.h" +#include "Renderer2d.h" +#include "list.h" +#include "mathlib.h" +#include + +void SummerTheme::load(Renderer2d* renderer) { + renderer->clearColor = Vector4(0, 181, 286, 255.f).toNormalizedColor(); + sun.sectors = 180; + sun.radius = renderer->context->width / 4.f; + sun.load(renderer); +} + +void SummerTheme::update(f32 dtSeconds) { + sun.update(dtSeconds); +} + +void SummerTheme::render(Renderer2d* renderer) { + sun.render(renderer); +} + +void SummerTheme::unload() { + sun.unload(); +} + +void Sun::load(Renderer2d* renderer) { + matte::List vertices; + matte::List indices; + Vector4 sunColor = Vector4(249, 215, 28, 255).toNormalizedColor(); + vertices.add({ Vector2(0, 0), sunColor, Mat4x4() }); + + f32 radiansPerSector = (2.f * PI) / sectors; + for (i32 i = 0; i <= sectors; i++) { + f32 radians = radiansPerSector * i; + f32 cosAngle = cosf(radians); + f32 sinAngle = sinf(radians); + Vector2 vertex = Vector2(radius * cosAngle, radius * sinAngle); + vertices.add({ vertex, sunColor, Mat4x4() }); + + u32 first = i; + u32 second = 0; + u32 third = i + 1; + indices.add(first); + indices.add(second); + indices.add(third); + } + + mesh.load(&vertices.data[0], vertices.numElements, &indices.data[0], indices.numElements, renderer); + mesh.model = Mat4x4().translateByVec2(Vector2(renderer->context->width / 2.f, renderer->context->height / 2.f)); + vertices.deallocate(); + indices.deallocate(); +} + +void Sun::update(f32 dtSeconds) { + +} + +void Sun::render(Renderer2d* renderer) { + setShaderMat4(renderer->uniforms.model, mesh.model); + glBindVertexArray(mesh.vao); + glDrawElements(GL_TRIANGLES, mesh.numIndices, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); +} + +void Sun::unload() { + mesh.unload(); +} diff --git a/themes/src/SummerTheme.h b/themes/src/SummerTheme.h new file mode 100644 index 0000000..1d9093a --- /dev/null +++ b/themes/src/SummerTheme.h @@ -0,0 +1,23 @@ +#pragma once +#include "types.h" +#include "Renderer2d.h" +#include + +struct Sun { + f32 radius = 20.f; + i32 sectors = 180; + Mesh2D mesh; + + void load(Renderer2d* renderer); + void update(f32 dtSeconds); + void render(Renderer2d* renderer); + void unload(); +}; + +struct SummerTheme { + Sun sun; + void load(Renderer2d* renderer); + void update(f32 dtSeconds); + void render(Renderer2d* renderer); + void unload(); +}; diff --git a/themes/src/TreeShape.cpp b/themes/src/TreeShape.cpp new file mode 100644 index 0000000..a3ae8f7 --- /dev/null +++ b/themes/src/TreeShape.cpp @@ -0,0 +1,214 @@ +#include "TreeShape.h" +#include "mathlib.h" +#include +#include +#include +#include + +void TreeBranchLoadData::fillVertices(Vertex2D* vertices, int branchTier) { + bottomLeft = Vector2 { position.x - width / 2.f, position.y }.rotateAround(rotation, position); + bottomRight = Vector2 { position.x + width / 2.f, position.y }.rotateAround(rotation, position); + topLeft = (Vector2 { position.x - width / 2.f, position.y + height }).rotateAround(rotation, position); + topRight = (Vector2 { position.x + width / 2.f, position.y + height }).rotateAround(rotation, position); + + topMidpoint = topLeft + (topRight - topLeft) / 2.f; + + vertices[0] = { bottomLeft, color}; + vertices[1] = { bottomRight, color}; + vertices[2] = { topLeft, color}; + vertices[3] = { topLeft, color}; + vertices[4] = { topRight, color}; + vertices[5] = { bottomRight, color}; +}; + +TreeShapeLoadResult TreeShape::load(Renderer2d* renderer) { + srand ( time(NULL) ); + + timeElapsedSeconds = 0; + + TreeLoadData ld; + + numBranches = pow(ld.divisionsPerBranch, ld.numBranchLevels + 1); + numVertices = 6 * numBranches; + + TreeBranchLoadData* generationData = new TreeBranchLoadData[numBranches]; + updateData = new TreeBranchUpdateData[numBranches]; + vertices = new Vertex2D[numVertices]; + + // The load result will contain information that we can pass on to our leaf renderer. + TreeShapeLoadResult lr; + lr.lowerBounds = Vector2(FLT_MAX, FLT_MAX); + lr.upperBounds = Vector2(FLT_MIN, FLT_MIN); + lr.updateData = updateData; + lr.numBranches = numBranches; + i32 branchIndex = 0; + createBranch(&ld, generationData, numBranches, &branchIndex, 0, ld.trunkWidth, ld.trunkHeight, Vector2 { 300.f, 50.f }, 0, NULL, vertices, &lr); + + useShader(renderer->shader); + + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, numVertices * sizeof(Vertex2D), &vertices[0], GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(renderer->attributes.position); + glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)0); + + glEnableVertexAttribArray(renderer->attributes.color); + glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)offsetof(Vertex2D, color)); + + for (i32 idx = 0; idx < 4; idx++) { + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (GLvoid *)(offsetof(Vertex2D, vMatrix) + (idx * 16))); + glVertexAttribDivisor(renderer->attributes.vMatrix + idx, 1); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + delete [] generationData; + + return lr; +} + +const f32 ninetyDegreeRotation = PI / 2.f; + +void TreeShape::createBranch(TreeLoadData* ld, TreeBranchLoadData* generationData, i32 numBranches, i32* branchIndex, + i32 branchLevel, f32 width, f32 height, Vector2 position, f32 rotation, + TreeBranchUpdateData* parent, Vertex2D* vertices, TreeShapeLoadResult* lr) { + TreeBranchLoadData* branchLoadData = &generationData[*branchIndex]; + branchLoadData->width = width; + branchLoadData->height = height; + branchLoadData->position = position; + branchLoadData->rotation = rotation; + branchLoadData->fillVertices(&vertices[(*branchIndex) * 6], branchLevel); + + // Fil in the bounds for the LeafRenderer later. + if (branchLoadData->topMidpoint.x > lr->upperBounds.x) { + lr->upperBounds.x = branchLoadData->topMidpoint.x; + } + if (branchLoadData->topMidpoint.y > lr->upperBounds.y) { + lr->upperBounds.y = branchLoadData->topMidpoint.y; + } + if (branchLoadData->topMidpoint.x < lr->lowerBounds.x) { + lr->lowerBounds.x = branchLoadData->topMidpoint.x; + } + if (branchLoadData->topMidpoint.y < lr->lowerBounds.y) { + lr->lowerBounds.y = branchLoadData->topMidpoint.y; + } + + TreeBranchUpdateData* branchUpdateData = &updateData[*branchIndex]; + branchUpdateData->tier = branchLevel; + branchUpdateData->periodOffset = randomFloatBetween(0.f, 2.f * PI); + branchUpdateData->period = randomFloatBetween(3.f, 5.f); + branchUpdateData->amplitude = randomFloatBetween(0.01f, 0.05f); + branchUpdateData->branchToFollow = parent; + branchUpdateData->vertices = &vertices[(*branchIndex) * 6]; + + if (branchLevel == ld->numBranchLevels) { + return; + } + + for (int division = 0; division < ld->divisionsPerBranch; division++) { + // Weight between [0, 1] + float weight = static_cast(division) / static_cast(ld->divisionsPerBranch - 1); + + // Normalize the weight between [-1, 1] + f32 normalizedWeight = (0.5f - (weight)) * 2.f; + + // We want a rotation that takes the current rotation of the branch, and averages it between the two branches. + f32 branchRotationAmount = randomFloatBetween(PI / 8.f, PI / 3.f); + f32 branchRotation = branchLoadData->rotation + (normalizedWeight * branchRotationAmount); + + // Since trees are taller vertically, we will find a normalized value that describes how far the direction is from + // being horizontal. If it is closer to 1, we will make the branch taller on average. + f32 verticalHeightScaler = (fabs(fabs(branchRotation) - ninetyDegreeRotation) / ninetyDegreeRotation) * 0.1; + f32 branchWidth = width * randomFloatBetween(ld->trunkWidthScalerMin, ld->trunkWidthScalerMax); + f32 branchHeight = height * randomFloatBetween(ld->trunkHeightScalerMin + verticalHeightScaler, ld->trunkHeightScalerMax + verticalHeightScaler); + + + // We want the branch to start within the previous branch, so we drop it down into it based off of the rotation. + Vector2 branchOffsetVertical = Vector2{ 0, branchWidth }.rotate(branchRotation); + + Vector2 branchPosition = branchLoadData->topLeft + ((branchLoadData->topRight - branchLoadData->topLeft) * weight) - branchOffsetVertical; // Position of branch along the top of the parent branch + + (*branchIndex)++; + createBranch(ld, generationData, numBranches, branchIndex, branchLevel + 1, branchWidth, branchHeight, branchPosition, branchRotation, branchUpdateData, vertices, lr); + } +} + +void TreeShape::update(f32 dtSeconds) { + timeElapsedSeconds += dtSeconds; + + for (i32 bIdx = 0; bIdx < numBranches; bIdx++) { + TreeBranchUpdateData* branchUpdataData = &updateData[bIdx]; + + // Fade in simulation. We fade in based on the tier. + f32 animationStart = (branchUpdataData->tier * animateStaggerPerTier); + f32 animationEnd = animationStart + animateTimePerTier; + + f32 alpha = 0.f; + if (timeElapsedSeconds < animationStart) { + alpha = 0.f; + } + else if (timeElapsedSeconds > animationEnd) { + alpha = 1.f; + } + else { + alpha = (1.f - (animationEnd - timeElapsedSeconds)) / animateTimePerTier; + } + + i32 startParentIndex = bIdx * 6; + + branchUpdataData->currentOffset.x = branchUpdataData->amplitude * cosf(branchUpdataData->periodOffset + branchUpdataData->period * timeElapsedSeconds); + branchUpdataData->currentOffset.y = branchUpdataData->amplitude * sinf(branchUpdataData->periodOffset + branchUpdataData->period * timeElapsedSeconds); + + if (branchUpdataData->branchToFollow != NULL) { + branchUpdataData->currentOffset += branchUpdataData->branchToFollow->currentOffset; + + // The root of the branch only moves according to the change of the end of the parent. + branchUpdataData->vertices[0].color.w = alpha; + branchUpdataData->vertices[0].position += branchUpdataData->branchToFollow->currentOffset; + branchUpdataData->vertices[1].color.w = alpha; + branchUpdataData->vertices[1].position += branchUpdataData->branchToFollow->currentOffset; + branchUpdataData->vertices[5].color.w = alpha; + branchUpdataData->vertices[5].position += branchUpdataData->branchToFollow->currentOffset; + } + else { + branchUpdataData->vertices[0].color.w = alpha; + branchUpdataData->vertices[1].color.w = alpha; + branchUpdataData->vertices[5].color.w = alpha; + } + + + branchUpdataData->vertices[2].color.w = alpha; + branchUpdataData->vertices[2].position += branchUpdataData->currentOffset; + branchUpdataData->vertices[3].color.w = alpha; + branchUpdataData->vertices[3].position += branchUpdataData->currentOffset; + branchUpdataData->vertices[4].color.w = alpha; + branchUpdataData->vertices[4].position += branchUpdataData->currentOffset; + } +} + +void TreeShape::render(Renderer2d* renderer) { + setShaderMat4(renderer->uniforms.model, model); + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Vertex2D), &vertices[0]); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, numVertices); + glBindVertexArray(0); +} + +void TreeShape::unload() { + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vbo); + delete[] vertices; + delete [] updateData; + timeElapsedSeconds = 0; + vertices = NULL; + updateData = NULL; +} diff --git a/themes/src/TreeShape.h b/themes/src/TreeShape.h new file mode 100644 index 0000000..32b00d3 --- /dev/null +++ b/themes/src/TreeShape.h @@ -0,0 +1,74 @@ +#include "Renderer2d.h" +#include "types.h" +#include "mathlib.h" + +struct TreeLoadData { + f32 trunkHeight = 96.f; // Height of the trunk start + f32 trunkWidth = 32.f; // Width of the trunk start + f32 trunkHeightScalerMin = 0.7f; + f32 trunkHeightScalerMax = 0.8f; + f32 trunkWidthScalerMin = 0.35f; + f32 trunkWidthScalerMax = 0.75f; + i32 divisionsPerBranch = 2; // How many branches to split into at each branch split + i32 numBranchLevels = 8; // How many branch levels to display +}; + +struct TreeBranchLoadData { + f32 width = 0.f; + f32 height = 0.f; + Vector2 position; // Center point + f32 rotation = 0; // How much we are rotated off of the center point in radians + Vector4 color = Vector4(101,56,24, 1.f).toNormalizedColor(); + + // Calculated while filling in vertices + Vector2 bottomLeft; + Vector2 bottomRight; + Vector2 topLeft; + Vector2 topRight; + Vector2 topMidpoint; + + void fillVertices(Vertex2D* vertices, int branchTier); +}; + +struct TreeBranchUpdateData { + i32 tier = 0; + f32 periodOffset = 0; + f32 period = 0; + f32 amplitude = 0; + Vector2 currentOffset; + Vertex2D* vertices = NULL; + TreeBranchUpdateData* branchToFollow = NULL; +}; + +struct TreeShapeLoadResult { + Vector2 lowerBounds; + Vector2 upperBounds; + Vector2 center; + TreeBranchUpdateData* updateData; + u32 numBranches = 0; +}; + +struct TreeShape { + // Update data + TreeBranchUpdateData* updateData = NULL; + Vertex2D* vertices = NULL; + f32 timeElapsedSeconds = 0.f; + f32 animateTimePerTier = 1.f; + f32 animateStaggerPerTier = 0.2f; + u32 numBranches = 0; + + // Render data + u32 vao; + u32 vbo; + u32 numVertices = 0; + Mat4x4 model; + + TreeShapeLoadResult load(Renderer2d* renderer); + void createBranch(TreeLoadData* ld, TreeBranchLoadData* branchList, i32 numBranches, + i32* branchIndex, i32 branchLevel, f32 width, f32 height, + Vector2 position, f32 rotation, TreeBranchUpdateData* parent, Vertex2D* vertices, TreeShapeLoadResult* lr); + void update(f32 dtSeconds); + void render(Renderer2d* renderer); + void unload(); +}; + diff --git a/themes/src/WebglContext.cpp b/themes/src/WebglContext.cpp new file mode 100644 index 0000000..df49c2d --- /dev/null +++ b/themes/src/WebglContext.cpp @@ -0,0 +1,46 @@ +#include "WebglContext.h" +#include + + +EM_BOOL onResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData) { + WebglContext* context = (WebglContext*)userData; + + f64 inWidth, inHeight; + emscripten_get_element_css_size(context->query, &inWidth, &inHeight); + + context->width = static_cast(inWidth); + context->height = static_cast(inHeight); + + return true; +} + +void WebglContext::init(const char* inQuery) { + strcpy(query, inQuery); + f64 inWidth, inHeight; + emscripten_get_element_css_size(query, &inWidth, &inHeight); + width = static_cast(inWidth); + height = static_cast(inHeight); + emscripten_set_canvas_element_size( query, width, height); + + EmscriptenWebGLContextAttributes attrs; + emscripten_webgl_init_context_attributes(&attrs); + + attrs.enableExtensionsByDefault = 1; + attrs.majorVersion = 3; + attrs.minorVersion = 0; + + context = emscripten_webgl_create_context(query, &attrs); + makeCurrentContext(); + + glClearColor(0, 0, 0, 0.0f); + + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, this, false, onResize); +}; + +void WebglContext::makeCurrentContext() { + emscripten_webgl_make_context_current(context); +}; + +void WebglContext::destroy() { + emscripten_webgl_destroy_context(context); +} diff --git a/themes/src/WebglContext.h b/themes/src/WebglContext.h new file mode 100644 index 0000000..1956092 --- /dev/null +++ b/themes/src/WebglContext.h @@ -0,0 +1,18 @@ +#pragma once +#include "types.h" +#include +#include +#include +#include +#include + +struct WebglContext { + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context; + f32 width = 800; + f32 height = 600; + char query[128];; + + void init(const char* inQuery); + void makeCurrentContext(); + void destroy() ; +}; diff --git a/themes/src/list.h b/themes/src/list.h new file mode 100644 index 0000000..25b236a --- /dev/null +++ b/themes/src/list.h @@ -0,0 +1,245 @@ +#pragma once +#include +#include +#include "Logger.h" + +#define FOREACH(list) \ + for (i32 idx = 0; idx < list.numElements; idx++) \ + if (auto value = &list[idx]) \ + +namespace matte { + template + struct List { + T* data = nullptr; + size_t capacity = 0; + size_t numElements = 0; + bool growDynamically = true; + + void allocate(size_t size); + void add(T& element); + void add(T&& element); + void add(T* element); + bool grow(size_t newSize); + void set(T& value, size_t index); + void set(T&& value, size_t index); + void set(T* value, size_t index); + void remove(size_t index); + void clear(); + void deallocate(); + bool isEmpty() { + return data == nullptr || numElements == 0; + } + T& getValue(int index) const; + T& operator[](int idx) const; + void binarySort(int (*f)(const T& first, const T& second)); + void setFromArray(T* arry, int size) { + allocate(size); + memcpy(data, arry, size * sizeof(T)); + numElements = size; + } + void remove(int index) { + if (index >= numElements) { + logger_error("Cannot remove element at index: %d", index); + return; + } + + + if (index == numElements - 1) { + numElements--; + return; + } + + memmove(&data[index], &data[index + 1], sizeof(T) * (numElements - index)); + numElements--; + } + }; + + template + void List::allocate(size_t size) { + if (size == 0 || size == capacity) { + numElements = 0; + return; + } + + if (data != nullptr) { + deallocate(); + } + + data = static_cast(malloc(sizeof(T) * size)); + capacity = size; + numElements = 0; + } + + template + bool List::grow(size_t newSize) { + if (!growDynamically) { + logger_error("Cannot grow list: growDynamically is disabled"); + return false; + } + + if (newSize == 0) { + logger_error("Cannot grow list: newSize is zero!"); + return false; + } + + T* newData = static_cast(malloc(sizeof(T) * newSize)); + + if (data != nullptr) { + memcpy(newData, data, numElements * sizeof(T)); + delete data; + } + + data = newData; + capacity = newSize; + return true; + } + + template + void List::set(T& value, size_t index) { + if (index >= capacity && !grow(index * 2)) { + return; + } + + memcpy(&data[index], value, sizeof(T)); + } + + template + void List::set(T&& value, size_t index) { + if (index >= capacity && !grow(index * 2)) { + return; + } + + data[index] = value; + } + + template + void List::set(T* value, size_t index) { + if (index >= capacity && !grow(index * 2)) { + return; + } + + memcpy(&data[index], value, sizeof(T)); + } + + template + void List::add(T* element) { + if (data == nullptr) { + allocate(2); + } + + if (element == nullptr) { + logger_error("Element not defined"); + return; + } + + size_t newNumElements = numElements + 1; + if (newNumElements > capacity) { + if (!grow(2 * capacity)) { + logger_error("Trying to add to list but unable to grow the array"); + return; + } + } + + memcpy(&data[numElements], element, sizeof(T)); + numElements = newNumElements; + } + + template + void List::add(T& element) { + if (data == nullptr) { + allocate(2); + } + + size_t newNumElements = numElements + 1; + if (newNumElements > capacity) { + if (!grow(2 * capacity)) { + logger_error("Trying to add to list but unable to grow the array"); + return; + } + } + + memcpy(&data[numElements], &element, sizeof(T)); + numElements = newNumElements; + } + + template + void List::add(T&& element) { + if (data == nullptr) { + allocate(2); + } + + size_t newNumElements = numElements + 1; + if (newNumElements > capacity) { + if (!grow(2 * capacity)) { + logger_error("Trying to add to list but unable to grow the array"); + return; + } + } + + memcpy(&data[numElements], &element, sizeof(T)); + numElements = newNumElements; + } + + template + void List::remove(size_t idx) { + if (idx >= numElements) { + logger_error("Index is outside of the list: %d >= %d", idx, numElements); + return; + } + + for (; idx < numElements - 1; idx++) { + data[idx] = data[idx + 1]; + } + + numElements--; + } + + template + void List::deallocate() { + if (data != nullptr) { + free(data); + data = nullptr; + } + + capacity = 0; + numElements = 0; + } + + template + void List::clear() { + numElements = 0; + } + + template + T& List::getValue(int idx) const { + return data[idx]; + } + + template + T& List::operator[](int idx) const { + return data[idx]; + } + + template + void List::binarySort(int (*f)(const T& first, const T& second)) { + if (data == nullptr) { + return; + } + + for (size_t idx = 0; idx < numElements - 1; idx++) { + int minIdx = idx; + T firstValue = data[idx]; + + for (int innerIdx = idx + 1; innerIdx < numElements; innerIdx++) {\ + T secondValue = data[innerIdx]; + if (f(data[idx], data[innerIdx]) > 0) { + minIdx= innerIdx; + } + } + + T temp = data[minIdx]; + memmove(&data[minIdx], &data[idx], sizeof(T)); + memmove(&data[idx], &temp, sizeof(T)); + } + } +} diff --git a/themes/src/main.cpp b/themes/src/main.cpp new file mode 100644 index 0000000..f8771d4 --- /dev/null +++ b/themes/src/main.cpp @@ -0,0 +1,445 @@ +#include "WebglContext.h" +#include "MainLoop.h" +#include "Renderer2d.h" +#include "Renderer3d.h" +#include "mathlib.h" +#include "types.h" +#include "TreeShape.h" +#include "SummerTheme.h" +#include "LeafParticleRender.h" +#include "Snowflake.h" +#include +#include + + +enum Theme { + Default = 0, + Autumn, + Winter, + Spring, + Summer +}; + +struct AutumnTheme { + TreeShape tree; + LeafParticleRender leafParticles; + + void load(Renderer2d* renderer); + void update(f32 dtSeconds); + void render(Renderer2d* renderer); + void unload(); +}; + +struct WinterTheme { + SnowflakeParticleRenderer spr; + + void load(Renderer2d* renderer); + void update(f32 dtSeconds); + void render(Renderer2d* renderer); + void unload(); +}; + +enum class BunnyAnimationState { + Loading = 0, + Loaded, + PreHop, + Hopping, + Idle +}; + +struct SpringTheme { + BunnyAnimationState state; + f32 bunnySpeed = 5.f; + Vector3 bunnyPosition = Vector3(0, 0, 0); + Vector3 bunnyTarget = Vector3(0, 0, 0); + Vector3 hopIncrement = Vector3(0, 0, 0); + + f32 numHops = 0; + f32 hopCount = 0; + f32 bunnyHopAnimationTimer = 0.f; + f32 stateTimer = 0.f; + f32 bunnyRotation = 0.f; + f32 targetRotation = 0.f; + + Mesh3d bunnyMesh; + + void load(Renderer3D* renderer); + void update(f32 dtSeconds); + void render(Renderer3D* renderer); + void unload(); +}; + +void load(Theme theme); +void unload(); +void update(f32 dtSeconds, void* userData); +EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); +EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData); + +WebglContext context; +Renderer2d renderer2d; +Renderer3D renderer3d; +MainLoop mainLoop; +Theme activeTheme = Theme::Default; +AutumnTheme autumnTheme; +WinterTheme winterTheme; +SpringTheme springTheme; +SummerTheme summerTheme; + +int main() { + context.init("#theme_canvas"); + emscripten_set_click_callback("#theme_button_default", NULL, false, selectNone); + emscripten_set_click_callback("#theme_button_autumn", NULL, false, selectAutumn); + emscripten_set_click_callback("#theme_button_winter", NULL, false, selectWinter); + emscripten_set_click_callback("#theme_button_spring", NULL, false, selectSpring); + emscripten_set_click_callback("#theme_button_summer", NULL, false, selectSummer); + + return 0; +} + +// -- Scene loading, updating, and unloading logic +void load(Theme theme) { + if (activeTheme == theme) { + printf("This theme is already active.\n"); + return; + } + + unload(); // Try and unload before we load, so that we start fresh + + activeTheme = theme; + mainLoop.run(update); + + switch (activeTheme) { + case Theme::Autumn: + renderer2d.load(&context); + autumnTheme.load(&renderer2d); + break; + case Theme::Winter: + renderer2d.load(&context); + winterTheme.load(&renderer2d); + break; + case Theme::Spring: + renderer3d.load(&context); + springTheme.load(&renderer3d); + break; + case Theme::Summer: + renderer2d.load(&context); + summerTheme.load(&renderer2d); + break; + default: + break; + } +} + +void update(f32 dtSeconds, void* userData) { + // -- Update + switch (activeTheme) { + case Theme::Autumn: + autumnTheme.update(dtSeconds); + break; + case Theme::Winter: + winterTheme.update(dtSeconds); + break; + case Theme::Spring: + springTheme.update(dtSeconds); + break; + case Theme::Summer: + summerTheme.update(dtSeconds); + break; + default: + break; + } + + // -- Render + switch (activeTheme) { + case Theme::Autumn: + renderer2d.render(); + autumnTheme.render(&renderer2d); + break; + case Theme::Winter: + renderer2d.render(); + winterTheme.render(&renderer2d); + break; + case Theme::Spring: + renderer3d.render(); + springTheme.render(&renderer3d); + break; + case Theme::Summer: + renderer2d.render(); + summerTheme.render(&renderer2d); + break; + default: + break; + } +} + +void unload() { + switch (activeTheme) { + case Theme::Autumn: + autumnTheme.unload(); + break; + case Theme::Winter: + winterTheme.unload(); + break; + case Theme::Spring: + springTheme.unload(); + break; + case Theme::Summer: + summerTheme.unload(); + break; + default: + break; + } + + activeTheme = Theme::Default; + if (mainLoop.isRunning) { + mainLoop.stop(); + renderer2d.unload(); + renderer3d.unload(); + } +} + +// -- HTML5 callbacks +EM_BOOL selectNone(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Default theme selected\n"); + unload(); + return true; +} + +EM_BOOL selectAutumn(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Autumn theme selected\n"); + load(Theme::Autumn); + return true; +} + +EM_BOOL selectWinter(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Winter theme selected\n"); + load(Theme::Winter); + return true; +} + +EM_BOOL selectSpring(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Spring theme selected\n"); + load(Theme::Spring); + return true; +} + +EM_BOOL selectSummer(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) { + printf("Summer theme selected\n"); + load(Theme::Summer); + return true; +} + +// -- Autumn theme +void AutumnTheme::load(Renderer2d* renderer) { + renderer->clearColor = Vector4(252, 210, 153, 255).toNormalizedColor(); + auto lr = tree.load(renderer); + leafParticles.load(renderer, &lr); +} + +void AutumnTheme::update(f32 dtSeconds) { + tree.update(dtSeconds); + leafParticles.update(dtSeconds); +} + +void AutumnTheme::render(Renderer2d* renderer) { + tree.render(renderer); + leafParticles.render(renderer); +} + +void AutumnTheme::unload() { + tree.unload(); + leafParticles.unload(); +} + +// -- Winter theme +void WinterTheme::load(Renderer2d* renderer) { + renderer->clearColor = Vector4(200, 229, 239, 255).toNormalizedColor(); + SnowflakeLoadParameters lp; + spr.load(lp, renderer); +} + +void WinterTheme::update(f32 dtSeconds) { + spr.update(dtSeconds); +} + +void WinterTheme::render(Renderer2d* renderer) { + spr.render(renderer); +} + +void WinterTheme::unload() { + spr.unload(); +} + +// -- Spring theme +void onBunnySuccess(emscripten_fetch_t *fetch) { + springTheme.state = BunnyAnimationState::Loaded; + printf("Finished downloading %llu bytes from URL %s.\n", fetch->numBytes, fetch->url); + const i32 len = fetch->numBytes; + springTheme.bunnyMesh = Mesh3d_fromObj(&renderer3d, fetch->data, len); + // The data is now available at fetch->data[0] through fetch->data[fetch->numBytes-1]; + emscripten_fetch_close(fetch); // Free data associated with the fetch. +} + +void onBunnyFail(emscripten_fetch_t *fetch) { + printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status); + emscripten_fetch_close(fetch); // Also free data on failure. +} + +void SpringTheme::load(Renderer3D* renderer) { + springTheme.state = BunnyAnimationState::Loading; + renderer->clearColor = Vector4(160, 231, 160, 255.f).toNormalizedColor(); + + emscripten_fetch_attr_t attr; + emscripten_fetch_attr_init(&attr); + strcpy(attr.requestMethod, "GET"); + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; + attr.onsuccess = onBunnySuccess; + attr.onerror = onBunnyFail; + emscripten_fetch(&attr, "themes/resources/bunny.obj"); +} + +inline Vector3 bunnyLerp(Vector3& start, Vector3& target, f32 t) { + t = 3 * t *t - 2 * t * t * t; + return start + ((target - start) * t); +} + +inline f32 verticalHopLerp(f32 start, f32 target, f32 t) { + f32 ogt = t; + t = 3 * t *t - 2 * t * t * t; + if (ogt >= 0.5f) t = 1.f - t; + return start + ((target - start) * t); +} + +inline f32 rotationLerp(f32 start, f32 target, f32 t) { + return start + ((target - start) * t); +} + +void SpringTheme::update(f32 dtSeconds) { + switch (state) { + case BunnyAnimationState::Loading: return; + case BunnyAnimationState::Loaded: + state = BunnyAnimationState::Idle; + stateTimer = 0.f; + bunnyHopAnimationTimer = 0.f; + break; + case BunnyAnimationState::Idle: { + bunnyHopAnimationTimer += dtSeconds; + const f32 HOP_FREQUENCY = 6.f; + + if (bunnyHopAnimationTimer > stateTimer) { + state = BunnyAnimationState::PreHop; + f32 xDir = 1; + f32 yDir = 1; + if (bunnyTarget.x > 0) xDir = -1; + if (bunnyTarget.z > 0) yDir = -1; + bunnyTarget = bunnyPosition + Vector3(randomFloatBetween(0, xDir * 25), 0, randomFloatBetween(0, yDir * 25)); + auto direction = (bunnyTarget - bunnyPosition); + auto distance = direction.length(); + direction = direction.normalize(); + numHops = ceil(distance / HOP_FREQUENCY); + hopCount = 0; + + targetRotation = PI - atan2(direction.y, direction.x); + stateTimer = ((bunnyTarget - bunnyPosition).length() / bunnySpeed) / numHops; + bunnyHopAnimationTimer = 0.f; + hopIncrement = (bunnyTarget - bunnyPosition) / numHops; + } + break; + } + case BunnyAnimationState::PreHop: { + const f32 ROTATION_TIME = 0.5f; + bunnyHopAnimationTimer += dtSeconds; + f32 current = bunnyRotation + (targetRotation - bunnyRotation) * (bunnyHopAnimationTimer / ROTATION_TIME); + bunnyMesh.model = Mat4x4().rotate(0, current, 0).translate(bunnyPosition); + + if (bunnyHopAnimationTimer > ROTATION_TIME) { + bunnyRotation = targetRotation; + bunnyHopAnimationTimer = 0; + state = BunnyAnimationState::Hopping; + } + break; + } + case BunnyAnimationState::Hopping: { + bunnyHopAnimationTimer += dtSeconds; + f32 t = bunnyHopAnimationTimer / stateTimer; + + Vector3 nextPosition = bunnyPosition + hopIncrement; + auto renderPos = bunnyLerp(bunnyPosition, nextPosition, t); + if ((renderPos - nextPosition).length() < 0.01f) { + hopCount += 1; + bunnyHopAnimationTimer = 0.f; + bunnyPosition = nextPosition; + } + + renderPos.y = verticalHopLerp(0.f, 4.f, t); + + const f32 RMAX = PI / 16.f; + f32 zRotation = 0; + f32 start = 0.f; + f32 end = PI / 8.f; + f32 startTime = 0.f; + f32 endTime = 0.f; + bool disableRot = false; + + if (t >= 0.9f) { + disableRot = true; + } + else if (t >= 0.7f) { + start = -RMAX; + end = 0.f; + startTime = 0.7f; + endTime = 0.9f; + } + else if (t >= 0.50f) { + start = 0.f; + end = -RMAX; + startTime = 0.50f; + endTime = 0.70f; + } + else if (t >= 0.40f) { + disableRot = true; + } + else if (t >= 0.20f) { + start = RMAX; + end = 0.f; + startTime = 0.20f; + endTime = 0.40f; + } + else { + start = 0.f; + end = RMAX; + startTime = 0.f; + endTime = 0.20f; + } + + + if (!disableRot) { + f32 totalTime = endTime - startTime; + zRotation = rotationLerp(start, end, (totalTime - (endTime - t)) / totalTime); + } + + bunnyMesh.model = Mat4x4().getZRotationMatrix(zRotation).rotate(0, bunnyRotation, 0).translate(renderPos); + if (hopCount == numHops) { + bunnyPosition = bunnyTarget; + bunnyHopAnimationTimer = 0.f; + state = BunnyAnimationState::Idle; + stateTimer = randomFloatBetween(0.5f, 1.f); + } + break; + } + } +} + +void SpringTheme::render(Renderer3D* renderer) { + renderer->render(); + if (state != BunnyAnimationState::Loading) { + bunnyMesh.render(renderer); + } +} + +void SpringTheme::unload() { + bunnyMesh.unload(); +} diff --git a/themes/src/mathlib.cpp b/themes/src/mathlib.cpp new file mode 100644 index 0000000..9e86833 --- /dev/null +++ b/themes/src/mathlib.cpp @@ -0,0 +1,779 @@ +// mathlib.cpp +// +// Created by Matt Kosarek +// +// A file containing some common math functionality that I find +// useful in my projects. I don't like that I don't know what's happening +// in glm, so I wrote most of this myself. All mistakes are my own. +// + +#include "mathlib.h" +#include + +// *************************************** +// Vector2 +Vector2::Vector2() { } + +Vector2::Vector2(float inX, float inY) { + x = inX; + y = inY; +} + +Vector2 getRandomNormalVector2() { + Vector2 retval = { + static_cast(rand()) / static_cast(RAND_MAX), + static_cast(rand()) / static_cast(RAND_MAX) + }; + + return retval.normalize(); +} + +Vector2 Vector2::operator+(Vector2 other) { + return { x + other.x, y + other.y }; +} + +Vector2& Vector2::operator+=(Vector2 other) { + x += other.x; + y += other.y; + return *this; +} + +Vector2 Vector2::operator-(Vector2 other) { + return { x - other.x, y - other.y }; +} + +Vector2 Vector2::operator*(float s) { + return { x * s, y * s }; +} + +Vector2 Vector2::operator/(float s) { + return { x / s, y / s }; +} + +float Vector2::dot(Vector2 other) { + return x * other.x + y * other.y; +} + +float Vector2::length() { + return sqrtf(x * x + y * y); +} + +Vector2 Vector2::normalize() { + float len = length(); + float inverseLength = len == 0 ? 1.0 : 1.0 / len; + + return { x * inverseLength, y * inverseLength }; +} + +Vector2 Vector2::negate() { + return { -x, -y }; +} + +Vector2 Vector2::getPerp() { + return { y, -x }; +} + +Vector2 Vector2::rotate(float angle) { + return { + x * cosf(angle) - y * sinf(angle), + x * sinf(angle) + y * cosf(angle) + }; +} + +Vector2 Vector2::rotateAround(float angle, const Vector2& other) { + Vector2 point = { x - other.x, y - other.y }; + point = { + point.x * cosf(angle) - point.y * sinf(angle), + point.x * sinf(angle) + point.y * cosf(angle) + }; + point = point + other; + return point; +} + +void Vector2::printDebug(const char* name) { + printf("%s=Vector2(%f, %f)\n", name, x, y); +} + + +// *************************************** +// Vector3 +Vector3::Vector3() { }; + +Vector3::Vector3(float value) { + x = value; + y = value; + z = value; +} + +Vector3::Vector3(float inX, float inY, float inZ) { + x = inX; + y = inY; + z = inZ; +} + +float Vector3::length() { + return sqrtf(x * x + y * y + z * z); +} + +float Vector3::dot(const Vector3& other) { + return x * other.x + y * other.y + z * other.z; +} + +Vector3 Vector3::scale(float scalar) { + return { + x * scalar, + y * scalar, + z * scalar + }; +} + +Vector3 Vector3::add(const Vector3& other) { + return { + x + other.x, + y + other.y, + z + other.z + }; +} + +Vector3 Vector3::subtract(const Vector3& other) { + return { + x - other.x, + y - other.y, + z - other.z + }; +} + +Vector3 Vector3::negate() { + return { + -x, + -y, + -z + }; +} + +Vector3 Vector3::cross(const Vector3& other) { + return { + y * other.z - z * other.y, + z * other.x - x * other.z, + x * other.y - y * other.x + }; +} + +Vector3 Vector3::normalize() { + float len = 1.f / length(); + return { + x * len, + y * len, + z * len + }; +} + +Vector3 Vector3::operator+(const Vector3& v2) { + return add(v2); +} + +Vector3& Vector3::operator+=(Vector3 other) { + x += other.x; + y += other.y; + z += other.z; + return *this; +} + +Vector3 Vector3::operator-(const Vector3& v2) { + return subtract(v2); +} + +Vector3 Vector3::operator-() { + return negate(); +} + +Vector3 Vector3::operator*(float value) { + return scale(value); +} + +Vector3 Vector3::operator/(const Vector3& v2) { + return { + x / v2.x, + y / v2.y, + z / v2.z + }; +} + +Vector3 Vector3::operator*(const Vector3& v2) { + return { + x * v2.x, + y * v2.y, + z * v2.z + }; +} + +float Vector3::operator [](int index) { + switch (index) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + default: + return 0; + } +} + +void Vector2::operator=(const Vector4& other) { + x = other.x; + y = other.y; +} + +void Vector3::printDebug(const char* name) { + printf("%s=Vector3(%f, %f, %f)\n", name, x, y, z); +} + + +// *************************************** +// Vector4 +Vector4::Vector4() { } + +Vector4::Vector4(float value) { + x = value; + y = value; + z = value; + w = value; +} + +Vector4::Vector4(float inX, float inY, float inZ, float inW) { + x = inX; + y = inY; + z = inZ; + w = inW; +} + +Vector4::Vector4(Vector2& v) { + x = v.x; + y = v.y; + z = 0; + w = 1; +} + +Vector4::Vector4(Vector3& v) { + x = v.x; + y = v.y; + z = v.z; + w = 1; +} + +Vector4 Vector4::fromColor(float r, float g, float b, float a) { + float scale = 1.f / 255.f; + return { r * scale, g * scale, b * scale, a * scale }; +} + +Vector4 Vector4::toNormalizedColor() { + return fromColor(x, y, z, w); +} + + +Vector3 Vector4::toVector3() { + return { x, y, z }; +} + +float Vector4::length() { + return sqrtf(x * x + y * y + z * z + w * w); +} + +Vector4 Vector4::normalize() { + float len = length(); + float inverseLength = len == 0 ? 1.0 : 1.0 / len; + + return { x * inverseLength, y * inverseLength, z * inverseLength, w * inverseLength }; +} + +float Vector4::dot(const Vector4& other) { + return x * other.x + y * other.y + z * other.z + w * other.w; +} + +Vector4 Vector4::scale(float scalar) { + return { + x * scalar, + y * scalar, + z * scalar, + w * scalar + }; +} + +Vector4 Vector4::add(const Vector4& other) { + return { + x + other.x, + y + other.y, + z + other.z, + w + other.w + }; +} + +Vector4 Vector4::subtract(const Vector4& other) { + return { + x - other.x, + y - other.y, + z - other.z, + w - other.w + }; +} + +Vector4 Vector4::negate() { + return { -x, -y, -z, -w }; +} + +Vector4 Vector4::cross(const Vector4& other) { + return { + y * other.z - z * other.y, + z * other.x - x * other.z, + x * other.y - y * other.x, + 1.f + }; +} + +Vector4 lerp(Vector4 start, Vector4 end, float t) { + return (end - start) * t + start; +} + +void Vector4::operator=(const Vector2& v2) { + x = v2.x; + y = v2.y; + z = 0; + w = 1; +} + +void Vector4::operator=(const Vector3& v2) { + x = v2.x; + y = v2.y; + z = v2.z; + w = 1; +} + +Vector4 Vector4::operator+(const Vector4& v2) { + return add(v2); +} + +Vector4 Vector4::operator-(const Vector4& v2) { + return subtract(v2); +} + +Vector4 Vector4::operator-() { + return negate(); +} + +Vector4 Vector4::operator*(float value) { + return scale(value); +} + +Vector4 Vector4::operator*(const Vector4& v2) { + return { + x * v2.x, + y * v2.y, + z * v2.z, + w * v2.w + }; +} + +float Vector4::operator[](int index) { + switch (index) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + return 0; + } +} + +void Vector4::printDebug(const char* name) { + printf("%s=Vector4(%f, %f, %f, %f)\n", name, x, y, z, w); +} + + +// *************************************** +// Mat4x4 +Mat4x4 Mat4x4::copy() { + Mat4x4 result; + memcpy(result.m, m, sizeof(float) * 16); + return result; +} + +Mat4x4 Mat4x4::scale(Vector3 v) { + Mat4x4 result = copy(); + result.m[0] = result.m[0] * v.x; + result.m[5] = result.m[5] *v.y; + result.m[10] = result.m[10] * v.z; + return result; +} + +Mat4x4 Mat4x4::translate(Vector3 v) { + Mat4x4 result = copy(); + result.m[12] += v.x; + result.m[13] += v.y; + result.m[14] += v.z; + return result; +} + +Mat4x4 Mat4x4::translateByVec2(Vector2 v) { + Mat4x4 result = copy(); + result.m[12] += v.x; + result.m[13] += v.y; + return result; +} + +Mat4x4 Mat4x4::rotate2D(float angle) { + Mat4x4 result = copy(); + result.m[0] = cos(angle); + result.m[1] = -sin(angle); + result.m[4] = sin(angle); + result.m[5] = cos(angle); + return result; +} + +Mat4x4 Mat4x4::getXRotationMatrix(float angleRadians) { + return { + { 1, 0, 0, 0, + 0, cos(angleRadians), -sin(angleRadians), 0, + 0, sin(angleRadians), cos(angleRadians), 0, + 0, 0, 0, 1 } + }; +} + +Mat4x4 Mat4x4::getYRotationMatrix(float angleRadians) { + return { + { cos(angleRadians), 0, sin(angleRadians), 0, + 0, 1, 0, 0, + -sin(angleRadians), 0, cos(angleRadians), 0, + 0, 0, 0, 1 } + }; +} + +Mat4x4 Mat4x4::getZRotationMatrix(float angleRadians) { + return { + { cos(angleRadians), -sin(angleRadians), 0, 0, + sin(angleRadians), cos(angleRadians), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 } + }; +} + +Mat4x4 Mat4x4::rotate(float xRadians, float yRadians, float zRadians) { + Mat4x4 result = copy(); + + Mat4x4 rotationMatrix; + if (xRadians != 0) { + rotationMatrix = getXRotationMatrix(xRadians); + result = result * rotationMatrix; + } + + if (yRadians != 0) { + rotationMatrix = getYRotationMatrix(yRadians); + result = result * rotationMatrix; + } + + if (zRadians != 0) { + rotationMatrix = getZRotationMatrix(zRadians); + result = result * rotationMatrix; + } + + return result; +} + +Vector2 Mat4x4::multByVec2(Vector2 v) { + Vector4 vec4 = { v.x, v.y, 0.0, 1.0 }; + return { + vec4.x * m[0] + vec4.y * m[4] + vec4.z * m[8] + vec4.w * m[12], + vec4.x * m[1] + vec4.y * m[5] + vec4.z * m[9] + vec4.w * m[13] + }; +} + +Vector2 Mat4x4::operator*(Vector2 v) { + return multByVec2(v); +} + +Mat4x4 Mat4x4::multMat4x4(const Mat4x4& other) { + Mat4x4 result; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + int row = i * 4; + result.m[row + j] = m[row + 0] * other.m[0 + j] + m[row + 1] * other.m[4 + j] + m[row + 2] * other.m[8 + j] + m[row + 3] * other.m[12 + j]; + } + } + + return result; +} + +Mat4x4 Mat4x4::operator*(const Mat4x4& other) { + return multMat4x4(other); +} + +Mat4x4 Mat4x4::getOrthographicMatrix(float left, float right, float bottom, float top) { + Mat4x4 result; + result.m[0] = 2.0 / (right - left); + result.m[5] = 2.0 / (top - bottom); + result.m[10] = 1.0; + result.m[12] = -(right + left) / (right - left); + result.m[13] = -(top + bottom) / (top - bottom); + return result; +} + +Mat4x4 Mat4x4::inverse() { + Mat4x4 inv; + + inv.m[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; + inv.m[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; + inv.m[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; + inv.m[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; + inv.m[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; + inv.m[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; + inv.m[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; + inv.m[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; + inv.m[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; + inv.m[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; + inv.m[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; + inv.m[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; + inv.m[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; + inv.m[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; + inv.m[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; + inv.m[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; + + float det = m[0] * inv.m[0] + m[1] * inv.m[4] + m[2] * inv.m[8] + m[3] * inv.m[12]; + + if (det == 0) + return Mat4x4(); + + det = 1.f / det; + + for (int i = 0; i < 16; i++) + inv.m[i] = inv.m[i] * det; + + return inv; +} + +Mat4x4 Mat4x4::getPerspectiveProjection(float near, float far, float fieldOfViewRadians, float aspectRatio) { + float halfFieldOfView = fieldOfViewRadians * 0.5f; + float top = tan(halfFieldOfView) * near; + float bottom = -top; + float right = top * aspectRatio; + float left = -right; + + return { + { (2 * near) / (right - left), 0, 0, 0, + 0, (2 * near) / (top - bottom), 0, 0, + (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1, + 0, 0, (-2 * far * near) / (far - near), 0 } + }; +} + +void Mat4x4::print() { + printf("[ "); + for (int idx = 0; idx < 16; idx++) { + printf("%f, ", m[idx]); + } + printf(" ]\n"); +} + +// See https://stackoverflow.com/questions/349050/calculating-a-lookat-matrix for why the dot product is in the bottom +Mat4x4 Mat4x4::getLookAt(Vector3 eye,Vector3 pointToLookAt, Vector3 up) { + Vector3 zAxis = (pointToLookAt - eye).normalize(); + Vector3 xAxis = zAxis.cross(up).normalize(); + Vector3 yAxis = xAxis.cross(zAxis).normalize(); + + return { + { xAxis.x, yAxis.x, -zAxis.x, 0, + xAxis.y, yAxis.y, -zAxis.y, 0, + xAxis.z, yAxis.z, -zAxis.z, 0, + -xAxis.dot(eye), -yAxis.dot(eye), zAxis.dot(eye), 1 } + }; +} + +// *************************************** +// Quaternion +Quaternion::Quaternion() { }; + +Quaternion::Quaternion(float inW, float inX, float inY, float inZ) { + w = inW; + x = inX; + y = inY; + z = inZ; +} + +float Quaternion::operator [](int index) { + switch (index) { + case 0: + return x; + case 1: + return y; + case 2: + return z; + case 3: + return w; + default: + return 0; + } +} + +Quaternion Quaternion::operator*(const Quaternion& other) const { + return { + w * other.w - x * other.x - y * other.y - z * other.z, // w + w * other.x + x * other.w + y * other.z - z * other.y, // i + w * other.y - x * other.z + y * other.w + z * other.x, // j + w * other.z + x * other.y - y * other.x + z * other.w // k + }; +} + +Quaternion Quaternion::operator*(const float& scale) const { + return { + w * scale, + x * scale, + y * scale, + z * scale + }; +} + +Quaternion Quaternion::operator+(const Quaternion& other) const { + return { + w + other.w, + x + other.x, + y + other.y, + z + other.z + }; +} + +Quaternion Quaternion::operator-(const Quaternion& other) const { + return { + w - other.w, + x - other.x, + y - other.y, + z - other.z + }; +} + +const float DOT_THRESHOLD = 0.9999f; + +// Using a slerp here, mostly taken from here: http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/. +// As JBlow says, it's expensive as heck. I will address this if it becomes a problem. +Quaternion Quaternion::interpolate(const Quaternion& other, const float factor) { + Quaternion newOther = other; + float dotProduct = dot(other); + + if (dotProduct < 0) { + newOther = other * -1; + dotProduct *= -1; + } + + if (dotProduct > DOT_THRESHOLD) { + return (*this + ((newOther - *this) * factor)).normalize(); + } + + float thetaZero = acos(dotProduct); // angle between input vectors + float theta = thetaZero * factor; + + Quaternion v2 = (newOther - (*this * dotProduct)).normalize(); + + return (*this * cos(theta)) + (v2 * sin(theta)); +} + +float Quaternion::length() const { + return sqrtf(x * x + y * y + z * z + w * w); +} + +Quaternion Quaternion::normalize() const { + float l = length(); + return { + w / l, + x / l, + y / l, + z / l, + }; +} + +/*Mat4x4 Quaternion::toMatrix() const { + return { + { + 1 - 2 * (y * y - z * z), + 2 * (x * y - z * w), + 2 * (x * z + w * y), + 0, + + 2 * (x * y + w * z), + 1 - 2 * (x * x - z * z), + 2 * (y * z - w * x), + 0, + + 2 * (x * z - w * y), + 2 * (y * z + w * x), + 1 - 2 * (x * x - y * y), + 0, + + 0, + 0, + 0, + 1 + } + }; +}*/ + +Mat4x4 Quaternion::toMatrix() const { + return { + { + 1 - 2 * (y * y + z * z), + 2 * (x * y + z * w), + 2 * (x * z - y * w), + 0, + 2 * (x * y - w * z), + 1 - 2 * (x * x + z * z), + 2 * (y * z + w * x), + 0, + 2 * (x * z + w * y), + 2 * (y * z - w * x), + 1 - 2 * (x * x + y * y), + 0, + 0, + 0, + 0, + 1 + } + }; +} + +float Quaternion::dot(const Quaternion& other) const { + return w * other.w + x * other.x + y * other.y + z * other.z; +} + +Quaternion quaternionFromRotation(Vector3 axis, float angleRadians) { + float halfAngleRadians = angleRadians / 2.f; + float cosHalfAngRad = cosf(halfAngleRadians); + float sinHalfAngRad = sinf(halfAngleRadians); + + return { + cosHalfAngRad, + axis.x * sinHalfAngRad, + axis.y * sinHalfAngRad, + axis.z * sinHalfAngRad + }; +} + +Quaternion quaternionFromEulerAngle(float yaw, float pitch, float roll) { + float cy = cosf(yaw * 0.5f); + float sy = sinf(yaw * 0.5f); + float cp = cosf(pitch * 0.5f); + float sp = sinf(pitch * 0.5f); + float cr = cosf(roll * 0.5f); + float sr = sinf(roll * 0.5f); + + return { + cr * cp * cy + sr * sp * sy, + sr * cp * cy - cr * sp * sy, + cr * sp * cy + sr * cp * sy, + cr * cp * sy - sr * sp * cy + }; +} + +void Quaternion::printDebug(const char* name) { + printf("%s=Quaternion(%f, %f, %f, %f)\n", name, x, y, z, w); +} diff --git a/themes/src/mathlib.h b/themes/src/mathlib.h new file mode 100644 index 0000000..bb62f39 --- /dev/null +++ b/themes/src/mathlib.h @@ -0,0 +1,181 @@ +// mathlib.h +// +// Created by Matt Kosarek +// +// A file containing some common math functionality that I find +// useful in my projects. I don't like that I don't know what's happening +// in glm, so I wrote most of this myself. All mistakes are my own. +// + +#pragma once +#include +#include +#include +#include + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define ABS(x) (x < 0 ? -x : x) +#define SIGN(x) (x < 0 ? -1 : 1) +#define PI 3.141592653589793238463 +#define E 2.71828182845904523536 +#define DEG_TO_RAD(x) (x * (PI / 180.f)) +#define RAD_TO_DEG(x) (x * (180.f / PI)) + +struct Vector4; + + +// -- Random +inline float randomFloatBetween(float min, float max) { + float random = static_cast(rand()) / static_cast(RAND_MAX); + return (max - min) * random + min; +} + +inline int randomIntBetween(int min, int max) { + return static_cast(randomFloatBetween(min, max)); +} + +struct Vector2 { + float x = 0; + float y = 0; + + Vector2(); + Vector2(float inX, float inY); + Vector2 operator+(Vector2 other); + Vector2& operator+=(Vector2 other); + Vector2 operator-(Vector2 other); + Vector2 operator*(float s); + Vector2 operator/(float s); + void operator=(const Vector4& other); + float dot(Vector2 other); + float length(); + Vector2 normalize(); + Vector2 negate(); + Vector2 getPerp(); + Vector2 rotate(float angle); + Vector2 rotateAround(float angle, const Vector2& other); + float determinant(Vector2 other); + + void printDebug(const char* name); +}; + +struct Vector3 { + float x = 0.f; + float y = 0.f; + float z = 0.f; + + Vector3(); + Vector3(float value); + Vector3(float x, float y, float z); + + float length(); + float dot(const Vector3& other); + Vector3 scale(float scalar); + Vector3 add(const Vector3& other); + Vector3 subtract(const Vector3& other); + Vector3 negate(); + Vector3 cross(const Vector3& other); + Vector3 normalize(); + + Vector3 operator+(const Vector3& v2); + Vector3& operator+=(Vector3 other); + Vector3 operator-(const Vector3& v2); + Vector3 operator-(); + Vector3 operator*(float value); + Vector3 operator*(const Vector3& v2); + Vector3 operator/(const Vector3& v2); + float operator[](int index); + + void printDebug(const char* name); +}; + +struct Vector4 { + float x = 0.f; + float y = 0.f; + float z = 0.f; + float w = 1.f; + + Vector4(); + Vector4(float value); + Vector4(float inX, float inY, float inZ, float inW); + Vector4 fromColor(float r, float g, float b, float a); + Vector4 toNormalizedColor(); + Vector4(Vector2& v); + Vector4(Vector3& v); + Vector3 toVector3(); + + float length(); + Vector4 normalize(); + float dot(const Vector4& other); + Vector4 scale(float scalar); + Vector4 add(const Vector4& other); + Vector4 subtract(const Vector4& other); + Vector4 negate(); + Vector4 cross(const Vector4& other); + + void operator=(const Vector2& v2); + void operator=(const Vector3& v2); + Vector4 operator+(const Vector4& v2); + Vector4 operator-(const Vector4& v2); + Vector4 operator-(); + Vector4 operator*(float value); + Vector4 operator*(const Vector4& v2); + float operator[](int index); + + void printDebug(const char* name); +}; + +Vector4 lerp(Vector4 start, Vector4 end, float t); + +struct Mat4x4 { + float m[16] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }; + + Mat4x4 copy(); + Mat4x4 scale(Vector3 v); + Mat4x4 translate(Vector3 v); + Mat4x4 translateByVec2(Vector2 v); + Mat4x4 rotate2D(float angle); + Mat4x4 getXRotationMatrix(float angleRadians); + Mat4x4 getYRotationMatrix(float angleRadians); + Mat4x4 getZRotationMatrix(float angleRadians); + Mat4x4 rotate(float xRadians, float yRadians, float zRadians); + Vector2 multByVec2(Vector2 v); + Vector2 operator*(Vector2 v); + Mat4x4 multMat4x4(const Mat4x4& other); + Mat4x4 operator*(const Mat4x4& other); + Mat4x4 getOrthographicMatrix(float left, float right, float bottom, float top); + Mat4x4 inverse(); + Mat4x4 getPerspectiveProjection(float near, float far, float fieldOfViewRadians, float aspectRatio); + Mat4x4 getLookAt(Vector3 eye,Vector3 pointToLookAt, Vector3 up); + void print(); +}; + +struct Quaternion { + float w = 1; + float x = 0; + float y = 0; + float z = 0; + + Quaternion(); + Quaternion(float inW, float inX, float inY, float inZ); + float operator [](int index); + Quaternion operator*(const Quaternion& other) const; + Quaternion operator*(const float& scale) const; + Quaternion operator+(const Quaternion& other) const; + Quaternion operator-(const Quaternion& other) const; + Quaternion interpolate(const Quaternion& other, const float factor); + Mat4x4 toMatrix() const; + Quaternion normalize() const; + float length() const; + float dot(const Quaternion& other) const; + + void printDebug(const char* name); +}; + +Quaternion quaternionFromRotation(Vector3 axis, float angleRadians); +Quaternion quaternionFromEulerAngle(float yaw, float pitch, float roll); diff --git a/themes/src/types.h b/themes/src/types.h new file mode 100644 index 0000000..ae8aa4e --- /dev/null +++ b/themes/src/types.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +typedef int8_t i8; +typedef int16_t i16; +typedef int32_t i32; +typedef int64_t i64; + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef unsigned long u64; + +typedef float f32; +typedef double f64; diff --git a/themes/types.h b/themes/types.h deleted file mode 100644 index ae8aa4e..0000000 --- a/themes/types.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -typedef int8_t i8; -typedef int16_t i16; -typedef int32_t i32; -typedef int64_t i64; - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef unsigned long u64; - -typedef float f32; -typedef double f64; -- cgit v1.2.1