Particle system
This commit is contained in:
parent
299272b7aa
commit
0d44ce87ff
80
particle.c
Normal file
80
particle.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
#include "particle.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "util/tree.h"
|
||||||
|
#include "util/vector.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
52
particle.h
Normal file
52
particle.h
Normal file
@ -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
|
25
particle_render.cpp
Normal file
25
particle_render.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
#include "camera.h"
|
||||||
|
#include "particle.h"
|
||||||
|
#include "app.h"
|
||||||
|
#include "render_util.h"
|
||||||
|
#include "util/assert.h"
|
||||||
|
#include <easyx.h>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
.rop2 = R2_COPYPEN,
|
||||||
.style = BS_SOLID,
|
.style = BS_SOLID,
|
||||||
.hatch = 0,
|
.hatch = 0,
|
||||||
.rotate = {.microseconds = 0},
|
.rotate = {.microseconds = 0},
|
||||||
.dissolve = {.microseconds = 0},
|
.dissolve = {.microseconds = 0},
|
||||||
.fadein = false};
|
.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) {
|
void render_SetModes(FillMode mode, TimePoint since) {
|
||||||
|
@ -49,7 +49,13 @@ typedef struct {
|
|||||||
// rop2 = R2_COPYPEN,
|
// rop2 = R2_COPYPEN,
|
||||||
// style = BS_SOLID,
|
// style = BS_SOLID,
|
||||||
// others = 0
|
// 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;
|
typedef struct _App App;
|
||||||
|
|
||||||
|
9
types.h
9
types.h
@ -4,6 +4,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -60,8 +61,12 @@ typedef struct {
|
|||||||
int64_t microseconds;
|
int64_t microseconds;
|
||||||
} Duration;
|
} Duration;
|
||||||
|
|
||||||
static inline double duration_Seconds(const Duration t) { return ((double)t.microseconds) / 1000000.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 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
|
// This function has a precision of at most 1ms under Windows. Sad
|
||||||
void duration_Sleep(const Duration t);
|
void duration_Sleep(const Duration t);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user