Snowstorm/src/snowstorm.c

201 lines
4.9 KiB
C

/* 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 <evan@moonside.games>
*
*/
#include "Snowstorm.h"
#include <stdlib.h>
#include <time.h>
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;
}