201 lines
4.9 KiB
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;
|
|
}
|