summaryrefslogtreecommitdiff
path: root/src/modules/sparkler/burst.c
blob: ae74e26d3757790d5c73444eeb36da4ac6b11eff (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include <stdarg.h>
#include <stdlib.h>

#include "bsp.h"
#include "burst.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 */

typedef struct _burst_ctxt_t {
#define PARAMS_DECLARE_STRUCT
#include "burst_params.def"
	int		age;
} burst_ctxt_t;


static int burst_init(particles_t *particles, const particles_conf_t *conf, particle_t *p, unsigned n_params, va_list params)
{
	burst_ctxt_t	*ctxt = p->ctxt;

#define PARAMS_ASSIGN_DEFAULTS
#include "burst_params.def"

	for (; n_params; n_params--) {
		switch (va_arg(params, burst_param_t)) {
#define PARAMS_IMPLEMENT_SWITCH
#include "burst_params.def"
		default:
			return 0;
		}
	}

	p->props->virtual = 1;
	ctxt->age = ctxt->duration;
	p->props->velocity = 0; /* burst should be stationary */
	p->props->mass = 0; /* no mass prevents gravity's effects */

	return 1;
}


static inline void thrust_part(particle_t *burst, particle_t *victim, float distance_sq)
{
	burst_ctxt_t	*ctxt = burst->ctxt;
	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 += ctxt->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->duration || (ctxt->duration--) <= 0) {
		return PARTICLE_DEAD;
	}

	/* affect neighbors for the shock-wave */
	/* TODO: ctxt->radius should probably describe the max radius, and the min - .01f... FIXME later. */
	s.radius_min = (1.0f - ((float)ctxt->duration / ctxt->age)) * ctxt->radius;
	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