diff options
author | mattkae <mattkae@protonmail.com> | 2022-01-16 18:32:50 -0500 |
---|---|---|
committer | mattkae <mattkae@protonmail.com> | 2022-01-16 18:32:50 -0500 |
commit | 1d47e3aa120539e053ffa41293f3f756b9d07844 (patch) | |
tree | 864069813b642d7634f83b6a8843c3d23a8d4926 /themes | |
parent | eef48388c610bf37b07aedef03c55344d450386b (diff) |
Successful beginnings of the winter theme
Diffstat (limited to 'themes')
-rw-r--r-- | themes/#LeafParticleRender.cpp# | 162 | ||||
-rw-r--r-- | themes/LeafParticleRender.cpp | 19 | ||||
-rw-r--r-- | themes/List.h | 222 | ||||
-rw-r--r-- | themes/Logger.cpp | 123 | ||||
-rw-r--r-- | themes/Logger.h | 43 | ||||
-rw-r--r-- | themes/Renderer2d.cpp | 9 | ||||
-rw-r--r-- | themes/Renderer2d.h | 2 | ||||
-rw-r--r-- | themes/Snowflake.cpp | 136 | ||||
-rw-r--r-- | themes/Snowflake.h | 46 | ||||
-rw-r--r-- | themes/TreeShape.cpp | 6 | ||||
-rw-r--r-- | themes/dist/output.js | 103 | ||||
-rwxr-xr-x | themes/dist/output.wasm | bin | 66787 -> 82223 bytes | |||
-rw-r--r-- | themes/main.cpp | 9 |
13 files changed, 842 insertions, 38 deletions
diff --git a/themes/#LeafParticleRender.cpp# b/themes/#LeafParticleRender.cpp# new file mode 100644 index 0000000..2cb0b35 --- /dev/null +++ b/themes/#LeafParticleRender.cpp# @@ -0,0 +1,162 @@ +#include "LeafParticleRender.h" +#include "Renderer2d.h" +#include "mathlib.h" +#include "TreeShape.h" +#include "types.h" +#include <math.h> + +const int32 verticesPerLeaf = 6; +const float32 leafRadius = 3.f; +const int32 fallChanceMax = 100; + +inline void updateLeaf(Renderer2dVertex* vertices, Vector2 position, Vector4 color, float32 scale) { + float32 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 }; + vertices[1] = { bottomRight, color }; + vertices[2] = { topLeft, color }; + vertices[3] = { topLeft, color }; + vertices[4] = { topRight, color }; + vertices[5] = { bottomRight, color }; +} + +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 Renderer2dVertex[numVertices]; + + for (int32 leafIdx = 0; leafIdx < numLeaves; leafIdx++) { + int32 randomBranch = randomIntBetween(0, lr->numBranches); + int32 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(Renderer2dVertex), &vertices[0], GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(renderer->attributes.position); + glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)0); + + glEnableVertexAttribArray(renderer->attributes.color); + glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)offsetof(Renderer2dVertex, color)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void LeafParticleRender::update(float32 dtSeconds) { + elapsedTimeSeconds += dtSeconds; + + // Every time the fallIntervalSeconds passes, we remove one leaf + // from the tree and send it barrelling towards the earth. + int32 fallRoll; + bool didGenerateFall = false; + if (elapsedTimeSeconds >= fallIntervalSeconds) { + fallRoll = randomIntBetween(0, fallChanceMax); + didGenerateFall = true; + elapsedTimeSeconds = 0; + } + + for (int32 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 float32 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(Renderer2dVertex), &vertices[0]); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, numVertices); + glBindVertexArray(0); +} + +void LeafParticleRender::unload() { + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vbo); + delete [] vertices; + delete [] updateData; + + elapsedTimeSeconds = 0; +}
\ No newline at end of file diff --git a/themes/LeafParticleRender.cpp b/themes/LeafParticleRender.cpp index 2fc89ac..e1ed1ec 100644 --- a/themes/LeafParticleRender.cpp +++ b/themes/LeafParticleRender.cpp @@ -16,12 +16,12 @@ inline void updateLeaf(Renderer2dVertex* vertices, Vector2 position, Vector4 col Vector2 topLeft = Vector2(-radius, radius) + position; Vector2 topRight = Vector2(radius, radius) + position; - vertices[0] = { bottomLeft, color }; - vertices[1] = { bottomRight, color }; - vertices[2] = { topLeft, color }; - vertices[3] = { topLeft, color }; - vertices[4] = { topRight, color }; - vertices[5] = { bottomRight, color }; + 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) { @@ -58,6 +58,11 @@ void LeafParticleRender::load(Renderer2d *renderer, TreeShapeLoadResult* lr) { glEnableVertexAttribArray(renderer->attributes.color); glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)offsetof(Renderer2dVertex, color)); + for (int32 idx = 0; idx < 4; idx++) { + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)(offsetof(Renderer2dVertex, vMatrix) + (idx * 16))); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } @@ -158,4 +163,4 @@ void LeafParticleRender::unload() { delete [] vertices; elapsedTimeSeconds = 0; -}
\ No newline at end of file +} diff --git a/themes/List.h b/themes/List.h new file mode 100644 index 0000000..00a466a --- /dev/null +++ b/themes/List.h @@ -0,0 +1,222 @@ +#pragma once +#include <cstdlib> +#include <cstring> +#include "Logger.h" + +#define FOREACH(list) \ + for (size_t idx = 0; idx < list.numElements; idx++) \ + if (auto value = list.getValue(idx)) \ + + +template <typename T> +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 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)(T *first, 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 <typename T> +void List<T>::allocate(size_t size) { + if (size == 0 || size == capacity) { + numElements = 0; + return; + } + + if (data != nullptr) { + deallocate(); + } + + data = static_cast<T*>(malloc(sizeof(T) * size)); + capacity = size; + numElements = 0; +} + +template <typename T> +bool List<T>::grow(size_t newSize) { + if (!growDynamically) { + return false; + } + + if (newSize == 0) { + return false; + } + + T* newData = static_cast<T*>(malloc(sizeof(T) * newSize)); + + if (data != nullptr) { + memcpy(newData, data, numElements * sizeof(T)); + delete data; + } + + data = newData; + capacity = newSize; + return true; +} + +template <typename T> +void List<T>::set(T* value, size_t index) { + if (index >= capacity && !grow(index * 2)) { + return; + } + + memcpy(&data[index], value, sizeof(T)); +} + +template <typename T> +void List<T>::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 <typename T> +void List<T>::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 <typename T> +void List<T>::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 <typename T> +void List<T>::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 <typename T> +void List<T>::deallocate() { + if (data != nullptr) { + free(data); + data = nullptr; + } + + capacity = 0; + numElements = 0; +} + +template <typename T> +void List<T>::clear() { + numElements = 0; +} + +template <typename T> +T* List<T>::getValue(int idx) const { + return &data[idx]; +} + +template <typename T> +T& List<T>::operator[](int idx) const { + return data[idx]; +} + +template <typename T> +void List<T>::binarySort(int (*f)(T *first, 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(&firstValue, &secondValue) > 0) { + minIdx= innerIdx; + } + } + + T temp = data[minIdx]; + memmove(&data[minIdx], &data[idx], sizeof(T)); + memmove(&data[idx], &temp, sizeof(T)); + } +} diff --git a/themes/Logger.cpp b/themes/Logger.cpp new file mode 100644 index 0000000..1068d88 --- /dev/null +++ b/themes/Logger.cpp @@ -0,0 +1,123 @@ +#include "Logger.h" +#include <chrono> +#include <cstdarg> +#include <cstdio> + +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 new file mode 100644 index 0000000..7596b6f --- /dev/null +++ b/themes/Logger.h @@ -0,0 +1,43 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include <cstring> + +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/Renderer2d.cpp b/themes/Renderer2d.cpp index a58ad86..d81b97b 100644 --- a/themes/Renderer2d.cpp +++ b/themes/Renderer2d.cpp @@ -9,11 +9,12 @@ const char* renderer2dVertexShader = "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 * vec4(position.x, position.y, 0, 1); \n" +" vec4 fragmentPosition = projection * model * vMatrix * vec4(position.x, position.y, 0, 1); \n" " gl_Position = fragmentPosition; \n" " VertexColor = color; \n" "}"; @@ -44,6 +45,7 @@ void Renderer2d::load(WebglContext* inContext) { 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); @@ -90,6 +92,11 @@ void Renderer2dShape::load(Renderer2dVertex* inVertices, uint32 inNumVertices, R glEnableVertexAttribArray(renderer->attributes.color); glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)offsetof(Renderer2dVertex, color)); + for (int32 idx = 0; idx < 4; idx++) { + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)(offsetof(Renderer2dVertex, vMatrix) + (idx * 16))); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } diff --git a/themes/Renderer2d.h b/themes/Renderer2d.h index cbdf4d4..8630169 100644 --- a/themes/Renderer2d.h +++ b/themes/Renderer2d.h @@ -16,6 +16,7 @@ struct Renderer2d { struct { int32 position; int32 color; + int32 vMatrix; } attributes; struct { @@ -31,6 +32,7 @@ struct Renderer2d { struct Renderer2dVertex { Vector2 position; Vector4 color; + Mat4x4 vMatrix; }; struct Renderer2dShape { diff --git a/themes/Snowflake.cpp b/themes/Snowflake.cpp index 54654f0..3c9afa4 100644 --- a/themes/Snowflake.cpp +++ b/themes/Snowflake.cpp @@ -1,26 +1,146 @@ #include "Snowflake.h" +#include "Renderer2d.h" +#include "mathlib.h" +#include "List.h" + +const Vector4 snowColor = Vector4(1.0, 0.98, 0.98, 1); + +inline void generateSnowflakeShape(List<Renderer2dVertex>* vertices, int32 numArms, float32 radius) { + float32 dx = ((2 * PI) / numArms) / 3.0; + for (int32 centerIdx = 0; centerIdx < (3 * numArms); centerIdx+=3) { + float32 degreeStart = dx * centerIdx; + float32 degreeEnd = dx * (centerIdx + 1); + + float32 cosStart = cosf(degreeStart); + float32 cosEnd = cosf(degreeEnd); + + float32 sinStart = sinf(degreeStart); + float32 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 rightStart = diff; + + 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, 6, randomFloatBetween(8.f, 24.f); + ud->numVertices = renderer->vertices.numElements - ud->vtxIdx; +} + +inline void spawnFlake(SnowflakeParticleRenderer* renderer, SnowflakeUpdateData* ud) { + ud->velocity = Vector2(randomFloatBetween(-10, 10), randomFloatBetween(-100, -85)); + ud->position = Vector2(randomFloatBetween(0, renderer->xMax), randomFloatBetween(renderer->yMax, renderer->yMax + 256)); +} + +inline void findAndSpawnNextFlake(SnowflakeParticleRenderer* renderer) { + do { + renderer->endIndex++; + + if (renderer->endIndex >= renderer->numSnowflakes) + renderer->endIndex = 0; + } while (renderer->updateData[renderer->endIndex].onGround); + + spawnFlake(renderer, &renderer->updateData[renderer->endIndex]); +} void SnowflakeParticleRenderer::load(SnowflakeLoadParameters params, Renderer2d* renderer) { - updateData = new SnowflakeUpdateData[maxSnowflakes]; - renderData = new SnowflakeRenderData[maxSnowflakes]; - activeIndex = 0; + startIndex = 0; + spawnIntervalSeconds = params.spawnIntervalSeconds; + endIndex = params.initialSnowflakeCount; + numSnowflakes = params.maxSnowflakes; + + updateData = new SnowflakeUpdateData[params.maxSnowflakes]; - for (int32 s = 0; s < maxSnowflakes; s++) { + xMax = static_cast<float32>(renderer->context->width); + yMax = static_cast<float32>(renderer->context->height); + + // Initialize each snow flake with its shape + for (int32 s = 0; s < numSnowflakes; s++) { auto ud = &updateData[s]; - auto rd = &renderData[s]; + initFlake(this, ud); + + if (s < endIndex) { + spawnFlake(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(Renderer2dVertex), &vertices.data[0], GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(renderer->attributes.position); + glVertexAttribPointer(renderer->attributes.position, 2, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)0); + + glEnableVertexAttribArray(renderer->attributes.color); + glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)offsetof(Renderer2dVertex, color)); - ud->velocity = params->flakeV0; + for (int32 idx = 0; idx < 4; idx++) { + int32 offset = (4 * sizeof(float32)) * idx; + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, + 4, + GL_FLOAT, + GL_FALSE, + sizeof(Renderer2dVertex), + (GLvoid *)(offsetof(Renderer2dVertex, vMatrix) + offset)); + //glVertexAttribDivisor(renderer->attributes.vMatrix + idx, 1); } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); } void SnowflakeParticleRenderer::update(float32 dtSeconds) { - + timeUntilNextSpawnSeconds -= dtSeconds; + if (timeUntilNextSpawnSeconds < 0) { + timeUntilNextSpawnSeconds = spawnIntervalSeconds; + findAndSpawnNextFlake(this); + } + + for (int32 s = startIndex; s < endIndex; s++) { + SnowflakeUpdateData* ud = &updateData[s]; + ud->position += ud->velocity * dtSeconds; + + Mat4x4 m = Mat4x4().translateByVec2(ud->position); + for (int32 v = ud->vtxIdx; v < (ud->vtxIdx + ud->numVertices); v++) { + vertices.data[v].vMatrix = m; + } + } } void SnowflakeParticleRenderer::render(Renderer2d* renderer) { + auto startVertex = &updateData[startIndex]; + auto endVertex = &updateData[endIndex]; + int32 numVertices = (endVertex->vtxIdx + endVertex->numVertices) - startVertex->vtxIdx; + setShaderMat4(renderer->uniforms.model, model); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, numVertices * sizeof(Renderer2dVertex), &vertices.data[startVertex->vtxIdx]); + + glBindVertexArray(vao); + glDrawArrays(GL_TRIANGLES, 0, numVertices); + glBindVertexArray(0); } void SnowflakeParticleRenderer::unload() { - + glDeleteVertexArrays(1, &vao); + glDeleteBuffers(1, &vbo); + vertices.deallocate(); + delete [] updateData; } diff --git a/themes/Snowflake.h b/themes/Snowflake.h index 09b05db..08f1c0c 100644 --- a/themes/Snowflake.h +++ b/themes/Snowflake.h @@ -1,36 +1,50 @@ #include "types.h" #include "mathlib.h" +#include "List.h" struct Renderer2d; -struct SnowflakeUpdateData; -struct SnowflakeRenderData; +struct Renderer2dVertex; struct SnowflakeLoadParameters { - int32 maxSnowflakes = 10000; + int32 maxSnowflakes = 1000; + int32 initialSnowflakeCount = 100; float32 rateOfSnowfall = 0.1f; - Vector3 flakeV0 = { 0, 1, 0 }; + Vector2 flakeV0 = { 0, 1 }; float32 flakeSize = 5.f; float32 flakeSizeDeviation = 1.f; Vector4 snowColor = { 0.8, 0.8, 0.8, 1.0 }; + float32 spawnIntervalSeconds = 0.3; +}; + +struct SnowflakeUpdateData { + Vector2 v0; + Vector2 velocity; + Vector2 position; + float32 rotation; + bool onGround = false; + + int32 vtxIdx = 0; + int32 numVertices = 0; }; struct SnowflakeParticleRenderer { - int32 activeIndex = 0; + float32 xMax = 0; + float32 yMax = 0; + float32 spawnIntervalSeconds = 0.3; + int32 startIndex = 0; + int32 endIndex = 0; + int32 numSnowflakes = 0; Vector3 windSpeed = { 0, 0, 0 }; SnowflakeUpdateData* updateData; - SnowflakeRenderData* renderData; + float32 timeUntilNextSpawnSeconds = 0;; + + uint32 vao; + uint32 vbo; + Mat4x4 model; + List<Renderer2dVertex> vertices; - void load(SnowflakeLoadParameters params, Renderer2d* renderer);o + void load(SnowflakeLoadParameters params, Renderer2d* renderer); void update(float32 dtSeconds); void render(Renderer2d* renderer); void unload(); }; - -struct SnowflakeUpdateData { - Vector3 velocity; - Vector3 position; -}; - -struct SnowflakeRenderData { - -}; diff --git a/themes/TreeShape.cpp b/themes/TreeShape.cpp index 797ceca..08170ea 100644 --- a/themes/TreeShape.cpp +++ b/themes/TreeShape.cpp @@ -59,6 +59,12 @@ TreeShapeLoadResult TreeShape::load(Renderer2d* renderer) { glEnableVertexAttribArray(renderer->attributes.color); glVertexAttribPointer(renderer->attributes.color, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)offsetof(Renderer2dVertex, color)); + for (int32 idx = 0; idx < 4; idx++) { + glEnableVertexAttribArray(renderer->attributes.vMatrix + idx); + glVertexAttribPointer(renderer->attributes.vMatrix + idx, 4, GL_FLOAT, GL_FALSE, sizeof(Renderer2dVertex), (GLvoid *)(offsetof(Renderer2dVertex, vMatrix) + (idx * 16))); + glVertexAttribDivisor(renderer->attributes.vMatrix + idx, 1); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); diff --git a/themes/dist/output.js b/themes/dist/output.js index 9a83997..9eb0f70 100644 --- a/themes/dist/output.js +++ b/themes/dist/output.js @@ -1835,6 +1835,82 @@ var ASM_CONSTS = { return demangleAll(js); } + function _tzset_impl() { + var currentYear = new Date().getFullYear(); + var winter = new Date(currentYear, 0, 1); + var summer = new Date(currentYear, 6, 1); + var winterOffset = winter.getTimezoneOffset(); + var summerOffset = summer.getTimezoneOffset(); + + // Local standard timezone offset. Local standard time is not adjusted for daylight savings. + // This code uses the fact that getTimezoneOffset returns a greater value during Standard Time versus Daylight Saving Time (DST). + // Thus it determines the expected output during Standard Time, and it compares whether the output of the given date the same (Standard) or less (DST). + var stdTimezoneOffset = Math.max(winterOffset, summerOffset); + + // timezone is specified as seconds west of UTC ("The external variable + // `timezone` shall be set to the difference, in seconds, between + // Coordinated Universal Time (UTC) and local standard time."), the same + // as returned by stdTimezoneOffset. + // See http://pubs.opengroup.org/onlinepubs/009695399/functions/tzset.html + HEAP32[((__get_timezone())>>2)] = stdTimezoneOffset * 60; + + HEAP32[((__get_daylight())>>2)] = Number(winterOffset != summerOffset); + + function extractZone(date) { + var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); + return match ? match[1] : "GMT"; + }; + var winterName = extractZone(winter); + var summerName = extractZone(summer); + var winterNamePtr = allocateUTF8(winterName); + var summerNamePtr = allocateUTF8(summerName); + if (summerOffset < winterOffset) { + // Northern hemisphere + HEAP32[((__get_tzname())>>2)] = winterNamePtr; + HEAP32[(((__get_tzname())+(4))>>2)] = summerNamePtr; + } else { + HEAP32[((__get_tzname())>>2)] = summerNamePtr; + HEAP32[(((__get_tzname())+(4))>>2)] = winterNamePtr; + } + } + function _tzset() { + // TODO: Use (malleable) environment variables instead of system settings. + if (_tzset.called) return; + _tzset.called = true; + _tzset_impl(); + } + function _localtime_r(time, tmPtr) { + _tzset(); + var date = new Date(HEAP32[((time)>>2)]*1000); + HEAP32[((tmPtr)>>2)] = date.getSeconds(); + HEAP32[(((tmPtr)+(4))>>2)] = date.getMinutes(); + HEAP32[(((tmPtr)+(8))>>2)] = date.getHours(); + HEAP32[(((tmPtr)+(12))>>2)] = date.getDate(); + HEAP32[(((tmPtr)+(16))>>2)] = date.getMonth(); + HEAP32[(((tmPtr)+(20))>>2)] = date.getFullYear()-1900; + HEAP32[(((tmPtr)+(24))>>2)] = date.getDay(); + + var start = new Date(date.getFullYear(), 0, 1); + var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; + HEAP32[(((tmPtr)+(28))>>2)] = yday; + HEAP32[(((tmPtr)+(36))>>2)] = -(date.getTimezoneOffset() * 60); + + // Attention: DST is in December in South, and some regions don't have DST at all. + var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset(); + var winterOffset = start.getTimezoneOffset(); + var dst = (summerOffset != winterOffset && date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; + HEAP32[(((tmPtr)+(32))>>2)] = dst; + + var zonePtr = HEAP32[(((__get_tzname())+(dst ? 4 : 0))>>2)]; + HEAP32[(((tmPtr)+(40))>>2)] = zonePtr; + + return tmPtr; + } + function ___localtime_r(a0,a1 + ) { + return _localtime_r(a0,a1); + } + function _abort() { abort('native code called abort()'); } @@ -3017,6 +3093,10 @@ var ASM_CONSTS = { GLctx.currentProgram = program; } + function _glVertexAttribDivisor(index, divisor) { + GLctx['vertexAttribDivisor'](index, divisor); + } + function _glVertexAttribPointer(index, size, type, normalized, stride, ptr) { var cb = GL.currentContext.clientBuffers[index]; if (!GLctx.currentArrayBufferBinding) { @@ -3083,6 +3163,7 @@ function intArrayToString(array) { var asmLibraryArg = { + "__localtime_r": ___localtime_r, "abort": _abort, "emscripten_get_element_css_size": _emscripten_get_element_css_size, "emscripten_memcpy_big": _emscripten_memcpy_big, @@ -3130,6 +3211,7 @@ var asmLibraryArg = { "glShaderSource": _glShaderSource, "glUniformMatrix4fv": _glUniformMatrix4fv, "glUseProgram": _glUseProgram, + "glVertexAttribDivisor": _glVertexAttribDivisor, "glVertexAttribPointer": _glVertexAttribPointer, "setTempRet0": _setTempRet0, "time": _time @@ -3142,12 +3224,27 @@ var ___wasm_call_ctors = Module["___wasm_call_ctors"] = createExportWrapper("__w var _main = Module["_main"] = createExportWrapper("main"); /** @type {function(...*):?} */ +var _free = Module["_free"] = createExportWrapper("free"); + +/** @type {function(...*):?} */ +var _malloc = Module["_malloc"] = createExportWrapper("malloc"); + +/** @type {function(...*):?} */ var ___errno_location = Module["___errno_location"] = createExportWrapper("__errno_location"); /** @type {function(...*):?} */ var _fflush = Module["_fflush"] = createExportWrapper("fflush"); /** @type {function(...*):?} */ +var __get_tzname = Module["__get_tzname"] = createExportWrapper("_get_tzname"); + +/** @type {function(...*):?} */ +var __get_daylight = Module["__get_daylight"] = createExportWrapper("_get_daylight"); + +/** @type {function(...*):?} */ +var __get_timezone = Module["__get_timezone"] = createExportWrapper("_get_timezone"); + +/** @type {function(...*):?} */ var stackSave = Module["stackSave"] = createExportWrapper("stackSave"); /** @type {function(...*):?} */ @@ -3172,12 +3269,6 @@ var _emscripten_stack_get_end = Module["_emscripten_stack_get_end"] = function() }; /** @type {function(...*):?} */ -var _malloc = Module["_malloc"] = createExportWrapper("malloc"); - -/** @type {function(...*):?} */ -var _free = Module["_free"] = createExportWrapper("free"); - -/** @type {function(...*):?} */ var dynCall_jiji = Module["dynCall_jiji"] = createExportWrapper("dynCall_jiji"); diff --git a/themes/dist/output.wasm b/themes/dist/output.wasm Binary files differindex 495ea67..427f834 100755 --- a/themes/dist/output.wasm +++ b/themes/dist/output.wasm diff --git a/themes/main.cpp b/themes/main.cpp index 8ca7fc4..8f908a3 100644 --- a/themes/main.cpp +++ b/themes/main.cpp @@ -5,6 +5,7 @@ #include "types.h" #include "TreeShape.h" #include "LeafParticleRender.h" +#include "Snowflake.h" enum Theme { Default = 0, @@ -23,6 +24,8 @@ struct AutumnTheme { }; struct WinterTheme { + SnowflakeParticleRenderer spr; + void load(Renderer2d* renderer); void update(float32 dtSeconds); void render(Renderer2d* renderer); @@ -167,13 +170,19 @@ void AutumnTheme::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(float32 dtSeconds) { + spr.update(dtSeconds); } void WinterTheme::render(Renderer2d* renderer) { + spr.render(renderer); } void WinterTheme::unload() { + spr.unload(); } |