summaryrefslogtreecommitdiff
path: root/src/modules/sparkler/burst.c
blob: b5ad3652a189b6e83aebc0db7b1188df83f88359 (plain)
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;
	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, 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,
		};
© All Rights Reserved