From 0d44ce87ffcee5c08e140492452c4b40bd838a9a Mon Sep 17 00:00:00 2001 From: Edgaru089 Date: Mon, 25 Mar 2024 14:59:00 +0800 Subject: [PATCH] Particle system --- particle.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ particle.h | 52 +++++++++++++++++++++++++++++ particle_render.cpp | 25 ++++++++++++++ render_util.cpp | 9 ++++- render_util.h | 8 ++++- types.h | 9 +++-- 6 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 particle.c create mode 100644 particle.h create mode 100644 particle_render.cpp diff --git a/particle.c b/particle.c new file mode 100644 index 0000000..93f06cc --- /dev/null +++ b/particle.c @@ -0,0 +1,80 @@ + +#include "particle.h" +#include "types.h" +#include "util/tree.h" +#include "util/vector.h" +#include + + +System_Particle *particle_NewSystem(App *super) { + System_Particle *sys = zero_malloc(sizeof(System_Particle)); + + sys->super = super; + sys->parts = tree_Create(sizeof(Particle)); + sys->deleted_ids = vector_Create(sizeof(uintptr_t)); + sys->maxID = 0; + return sys; +} +void particle_DeleteSystem(System_Particle *sys) { + tree_Destroy(sys->parts); + vector_Destroy(sys->deleted_ids); + free(sys); +} + + +static void _particle_AdvancePart(System_Particle *sys, Particle *p, Duration deltaTime) { + p->size -= p->sizedec * duration_Seconds(deltaTime); + if ((p->tolive.microseconds != 0 && + time_Since(p->addtime).microseconds > p->tolive.microseconds) || + p->size < EPS) { // vanished + particle_Delete(sys, p->id); + return; + } + + // Move + Vec2 delta = vec2_Scale(p->vec, duration_Seconds(deltaTime)); + p->pos = vec2_Add(p->pos, delta); + // Move slower + Vec2 delta_vec = vec2_Scale(p->vec, -p->vec_friction); + p->vec = vec2_Add(p->vec, delta_vec); +} + +void particle_Advance(System_Particle *sys, Duration deltaTime) { + // Clear particles marked as deleted + for (int i = 0; i < vector_Size(sys->deleted_ids); i++) { + uintptr_t id = *(uintptr_t *)vector_At(sys->deleted_ids, i); + tree_Node *n = tree_FindNode(sys->parts, id); + if (n != NULL) + tree_Delete(sys->parts, n); + else + fprintf(stderr, "[particle_Delete][WARN] Missing particle ID [%d]\n", id); + } + vector_Clear(sys->deleted_ids); + + for (tree_Node *i = tree_FirstNode(sys->parts); + i != NULL; + i = tree_Node_Next(i)) { + Particle *p = (Particle *)i->data; + _particle_AdvancePart(sys, p, deltaTime); + } +} + +void particle_Delete(System_Particle *sys, uintptr_t id) { + vector_Push(sys->deleted_ids, &id); +} + + +void particle_Emit(System_Particle *sys, Vec2 pos, Vec2 vec, double vec_friction, double size, double sizedec, Duration tolive, const FillMode *fill) { + uintptr_t id = ++sys->maxID; + Particle *p = tree_Insert(sys->parts, id, NULL); + memset(p, 0, sizeof(Particle)); + p->id = id; + p->addtime = time_Now(); + p->tolive = tolive; + p->pos = pos; + p->vec = vec; + p->vec_friction = vec_friction; + p->size = size; + p->sizedec = sizedec; + p->mode = fill; +} diff --git a/particle.h b/particle.h new file mode 100644 index 0000000..3ce6813 --- /dev/null +++ b/particle.h @@ -0,0 +1,52 @@ +#pragma once + +#include "types.h" +#include "render_util.h" +#include "util/tree.h" +#include "util/vector.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +// A particle instance. +typedef struct { + uintptr_t id; // ID. + double size, sizedec; // Diameter & decrease speed, size=0 and the particle is removed + Duration tolive; // Time desiginated to live, 0 for forever + TimePoint addtime; // Time when this particle is added + + Vec2 pos, vec; // Center & Speed + double vec_friction; // Friction, fraction of Vec per second + + const FillMode *mode; // Fill Modes, NULL for default mode (solid fill) +} Particle; + + +typedef struct _App App; + +// Particle system. +typedef struct { + App *super; + tree_Tree *parts; // uintptr_t -> struct Particle + vector_Vector *deleted_ids; + uintptr_t maxID; +} System_Particle; + +System_Particle *particle_NewSystem(App *super); +void particle_DeleteSystem(System_Particle *sys); + +void particle_Advance(System_Particle *sys, Duration deltaTime); +void particle_Render(System_Particle *sys); + + +// Emit a particle here. +void particle_Emit(System_Particle *sys, Vec2 pos, Vec2 vec, double vec_friction, double size, double sizedec, Duration tolive, const FillMode *fill); +void particle_Delete(System_Particle *sys, uintptr_t id); + + +#ifdef __cplusplus +} +#endif diff --git a/particle_render.cpp b/particle_render.cpp new file mode 100644 index 0000000..9787e4d --- /dev/null +++ b/particle_render.cpp @@ -0,0 +1,25 @@ + +#include "camera.h" +#include "particle.h" +#include "app.h" +#include "render_util.h" +#include "util/assert.h" +#include + + +extern "C" { + +void particle_Render(System_Particle *sys) { + ASSERT(sys->super->camera && "particle_Render called without a Camera system"); + System_Camera *cam = sys->super->camera; + + for (tree_Node *i = tree_FirstNode(sys->parts); + i != NULL; + i = tree_Node_Next(i)) { + Particle *p = (Particle *)i->data; + + render_SetModes(*p->mode, p->addtime); + render_FillCircleW(sys->super, p->pos, p->size); + } +} +} diff --git a/render_util.cpp b/render_util.cpp index 51c411d..55c3bf0 100644 --- a/render_util.cpp +++ b/render_util.cpp @@ -47,13 +47,20 @@ void render_DrawText(int x, int y, const char *str) { } -const FillMode render_DefaultMode = { +const FillMode render_ModeDefault = { .rop2 = R2_COPYPEN, .style = BS_SOLID, .hatch = 0, .rotate = {.microseconds = 0}, .dissolve = {.microseconds = 0}, .fadein = false}; +const FillMode render_ModeInverse = { + .rop2 = R2_NOT, + .style = BS_SOLID, + .hatch = 0, + .rotate = {.microseconds = 0}, + .dissolve = {.microseconds = 0}, + .fadein = false}; void render_SetModes(FillMode mode, TimePoint since) { diff --git a/render_util.h b/render_util.h index 4f1dd6c..6f2ced3 100644 --- a/render_util.h +++ b/render_util.h @@ -49,7 +49,13 @@ typedef struct { // rop2 = R2_COPYPEN, // style = BS_SOLID, // others = 0 -extern const FillMode render_DefaultMode; +extern const FillMode render_ModeDefault; + +// Fill mode that inverses the screen. +// rop2 = R2_NOT, +// style = BS_SOLID, +// others = 0 +extern const FillMode render_ModeInverse; typedef struct _App App; diff --git a/types.h b/types.h index 8779280..21816e7 100644 --- a/types.h +++ b/types.h @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -60,8 +61,12 @@ typedef struct { int64_t microseconds; } Duration; -static inline double duration_Seconds(const Duration t) { return ((double)t.microseconds) / 1000000.0; } -static inline double duration_Milliseconds(const Duration t) { return ((double)t.microseconds) / 1000.0; } +static inline double duration_Seconds(const Duration t) { return ((double)t.microseconds) / 1000000.0; } +static inline double duration_Milliseconds(const Duration t) { return ((double)t.microseconds) / 1000.0; } +static inline Duration duration_FromSeconds(double seconds) { + Duration d = {.microseconds = (int64_t)round(seconds * 1000.0 * 1000.0)}; + return d; +} // This function has a precision of at most 1ms under Windows. Sad void duration_Sleep(const Duration t);