/* Snowstorm - Particle effects in C * * Copyright (c) 2021 Evan Hemsley * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * * Evan "cosmonaut" Hemsley * */ #include "Snowstorm.h" #include #include typedef struct Snowstorm_Vector2 { float x; float y; } Snowstorm_Vector2; static inline Snowstorm_Vector2 Vector2_Rotate(Snowstorm_Vector2 vector, float angle) { Snowstorm_Vector2 rotated; rotated.x = vector.x * cosf(angle) - vector.y * sinf(angle); rotated.y = vector.x * sinf(angle) + vector.y * cosf(angle); return rotated; } static inline float Vector2_Magnitude(Snowstorm_Vector2 vector) { return sqrtf(vector.x * vector.x + vector.y * vector.y); } static inline Snowstorm_Vector2 Vector2_Normalize(Snowstorm_Vector2 vector) { float length = Vector2_Magnitude(vector); Snowstorm_Vector2 normalized; normalized.x = vector.x / length; normalized.y = vector.y / length; return normalized; } typedef struct Snowstorm_Particle { float time; Snowstorm_Vector2 position; Snowstorm_Vector2 velocity; Snowstorm_Vector2 scale; float size; float rotation; float spin; float directionVariance; float speedVariance; Snowstorm_Vector2 catchup; } Snowstorm_Particle; typedef struct Snowstorm_Context { Snowstorm_Particle** particles; uint32_t particleCount; Snowstorm_Vector2 wind; float directionChaos; float speedChaos; float leftBound; float topBound; float rightBound; float bottomBound; } Snowstorm_Context; static Snowstorm_Context* context = NULL; static inline float Approach(float value, float target, float maxChange) { return value + min(max(target - value, -maxChange), maxChange); } static inline float WrapWithOvershoot(float value, float min, float max) { if (value < min) { value = max - (min - value); } if (value > max) { value = min + (value - max); } return value; } static inline float Random(float a) { return (float)rand() / (float)(RAND_MAX / a); } static inline float RandomRange(float min, float max) { return Random(max - min) + min; } void Snowstorm_Init() { srand((unsigned int)time(NULL)); context = malloc(sizeof(Snowstorm_Context)); context->particles = NULL; context->particleCount = 0; context->leftBound = -10; context->rightBound = 490; context->topBound = -10; context->bottomBound = 280; context->wind.x = 0; context->wind.y = 0; context->directionChaos = 0; context->speedChaos = 0; } void Snowstorm_Update(double delta) { uint32_t i; Snowstorm_Particle* particle = NULL; float deltaTime = (float)delta; for (i = 0; i < context->particleCount; i += 1) { particle = context->particles[i]; if (particle != NULL) { particle->time += deltaTime; float speed = Vector2_Magnitude(particle->velocity); float rotation = sinf(particle->time / (speed * 100)) * particle->directionVariance; float windSpeed = Vector2_Magnitude(context->wind); Snowstorm_Vector2 wind = Vector2_Normalize(context->wind); wind.x *= (windSpeed + particle->speedVariance); wind.y *= (windSpeed + particle->speedVariance); wind = Vector2_Rotate(wind, rotation); particle->velocity.x = Approach(particle->velocity.x, wind.x, particle->catchup.x); particle->velocity.y = Approach(particle->velocity.y, wind.y, particle->catchup.y); particle->position.x = WrapWithOvershoot(particle->position.x + particle->velocity.x, context->leftBound, context->rightBound); particle->position.y = WrapWithOvershoot(particle->position.y + particle->velocity.y, context->topBound, context->bottomBound); particle->scale.y = particle->size * sinf(particle->time / 10); particle->rotation += particle->spin; } } } void Snowstorm_ApplyWind(double xSpeed, double ySpeed) { uint32_t i; Snowstorm_Particle* particle; for (i = 0; i < context->particleCount; i += 1) { particle = context->particles[i]; if (particle != NULL) { particle->directionVariance = RandomRange(-context->directionChaos, context->directionChaos); particle->speedVariance = RandomRange(-context->speedChaos, context->speedChaos); } } context->wind.x = (float)xSpeed; context->wind.y = (float)ySpeed; }