1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
#include <stdlib.h>
#include "bsp.h"
#include "container.h"
#include "particle.h"
#include "particles.h"
/* a "burst" (shockwave) particle type */
/* this doesn't draw anything, it just pushes neighbors away in an increasing radius */
#define BURST_FORCE 0.01f
#define BURST_MAX_LIFETIME 8
typedef struct _burst_ctxt_t {
int longevity;
int lifetime;
} burst_ctxt_t;
static int burst_init(particles_t *particles, const particles_conf_t *conf, particle_t *p)
{
burst_ctxt_t *ctxt = p->ctxt;
ctxt->longevity = ctxt->lifetime = BURST_MAX_LIFETIME;
p->props->virtual = 1;
return 1;
}
static inline void thrust_part(particle_t *burst, particle_t *victim, float distance_sq)
{
v3f_t direction = v3f_sub(&victim->props->position, &burst->props->position);
/* TODO: normalize is expensive, see about removing these. */
direction = v3f_normalize(&direction);
victim->props->direction = v3f_add(&victim->props->direction, &direction);
victim->props->direction = v3f_normalize(&victim->props->direction);
victim->props->velocity += BURST_FORCE;
}
typedef struct burst_sphere_t {
particles_t *particles;
particle_t *center, *last;
til_fb_fragment_t *fragment;
float radius_min;
float radius_max;
unsigned trace_matches:1;
unsigned trace_affected:1;
} burst_sphere_t;
static void burst_cb(bsp_t *bsp, list_head_t *occupants, void *_s)
{
burst_sphere_t *s = _s;
bsp_occupant_t *o;
float rmin_sq = s->radius_min * s->radius_min;
float rmax_sq = s->radius_max * s->radius_max;
/* XXX: to avoid having a callback per-particle, bsp_occupant_t was
* moved to the public particle, and the particle-specific
* implementations directly perform bsp-accelerated searches. Another
* wart caused by this is particles_bsp().
*/
list_for_each_entry(o, occupants, occupants) {
particle_t *p = container_of(o, particle_t, occupant);
float d_sq;
if (p->props->virtual) {
/* don't move virtual particles (includes ourself) */
continue;
}
d_sq = v3f_distance_sq(&s->center->props->position, &p->props->position);
if (d_sq > rmin_sq && d_sq < rmax_sq) {
/* displace the part relative to the burst origin */
thrust_part(s->center, p, d_sq);
if (s->trace_affected) {
particles_draw_line(s->particles, &s->last->props->position, &p->props->position, s->fragment);
s->last = p;
}
}
if (s->trace_matches) {
particles_draw_line(s->particles, &s->last->props->position, &p->props->position, s->fragment);
s->last = p;
}
}
}
static particle_status_t burst_sim(particles_t *particles, const particles_conf_t *conf, particle_t *p, til_fb_fragment_t *f)
{
burst_ctxt_t *ctxt = p->ctxt;
bsp_t *bsp = particles_bsp(particles); /* XXX see note above about bsp_occupant_t */
burst_sphere_t s;
if (!ctxt->longevity || (ctxt->longevity--) <= 0) {
return PARTICLE_DEAD;
}
/* affect neighbors for the shock-wave */
s.radius_min = (1.0f - ((float)ctxt->longevity / ctxt->lifetime)) * 0.075f;
s.radius_max = s.radius_min + .01f;
s.center = s.last = p;
s.trace_matches = (conf->show_bsp_matches && !conf->show_bsp_matches_affected_only);
s.trace_affected = (conf->show_bsp_matches && conf->show_bsp_matches_affected_only);
s.particles = particles;
s.fragment = f;
bsp_search_sphere(bsp, &p->props->position, s.radius_min, s.radius_max, burst_cb, &s);
return PARTICLE_ALIVE;
}
particle_ops_t burst_ops = {
.context_size = sizeof(burst_ctxt_t),
.sim = burst_sim,
.init = burst_init,
.draw = NULL,
.cleanup = NULL,
};
|