diff options
| author | Vito Caputo <vcaputo@pengaru.com> | 2022-03-28 21:46:59 -0700 | 
|---|---|---|
| committer | Vito Caputo <vcaputo@pengaru.com> | 2022-03-28 22:03:18 -0700 | 
| commit | 6ad1fe1ad5400f25686e39b119615941377e0c71 (patch) | |
| tree | 1f4f5667dd2eb0958e3169df1f7bec781605842a /src | |
| parent | 605dc2454a5b6d8e1e203bea2d42ee7b1bd1a27f (diff) | |
modules/swarm: add lines drawing style
Introduce drawing styles, adding a line style in addition to the
existing points.
Settings are style={points,lines}, default is now lines.
Diffstat (limited to 'src')
| -rw-r--r-- | src/modules/swarm/swarm.c | 219 | 
1 files changed, 197 insertions, 22 deletions
| diff --git a/src/modules/swarm/swarm.c b/src/modules/swarm/swarm.c index 7c8caeb..8ff4812 100644 --- a/src/modules/swarm/swarm.c +++ b/src/modules/swarm/swarm.c @@ -27,8 +27,10 @@  #include "til.h"  #include "til_fb.h" -#define SWARM_SIZE	(32 * 1024) -#define SWARM_ZCONST	4.f +typedef enum swarm_draw_style_t { +	SWARM_DRAW_STYLE_POINTS,	/* simple opaque pixel per particle */ +	SWARM_DRAW_STYLE_LINES,		/* simple opaque lines per particle, oriented and sized by direction and velocity */ +} swarm_draw_style_t;  typedef struct v3f_t {  	float	x, y, z; @@ -50,6 +52,12 @@ typedef struct swarm_context_t {  	boid_t		boids[];  } swarm_context_t; +#define SWARM_SIZE		(32 * 1024) +#define SWARM_ZCONST		4.f +#define SWARM_DEFAULT_STYLE	SWARM_DRAW_STYLE_LINES + +static swarm_draw_style_t swarm_draw_style = SWARM_DEFAULT_STYLE; +  static inline float randf(float min, float max)  { @@ -111,6 +119,16 @@ static inline void v3f_normalize(v3f_t *v)  } +static inline v3f_t v3f_invert(v3f_t v) +{ +	return (v3f_t){ +		.x = -v.x, +		.y = -v.y, +		.z = -v.z, +	}; +} + +  static v3f_t v3f_lerp(v3f_t a, v3f_t b, float t)  {  	return (v3f_t){ @@ -184,10 +202,34 @@ static void swarm_update(swarm_context_t *ctxt, unsigned ticks)  	{ /* [0] = leader */  		float	r = M_PI * 2 * ((cosf((float)ticks * .001f) * .5f + .5f)); +		v3f_t	newpos = { +				.x = cosf(r), +				.y = sinf(r), +				.z = cosf(r * 2.f), +			}; +		boid_t	*b = &ctxt->boids[0]; + +		if (newpos.x != b->position.x || +		    newpos.y != b->position.y || +		    newpos.z != b->position.z) { + +			/* XXX: this must be conditional on position changing otherwise +			 * it could produce a zero direction vector, making normalize +			 * spit out NaN, and things fall apart. +			 */ + +			b->direction = v3f_sub(b->position, newpos); +			b->velocity = v3f_len(b->direction); +			v3f_normalize(&b->direction); +			b->position = newpos; -		ctxt->boids[0].position.x = cosf(r); -		ctxt->boids[0].position.y = sinf(r); -		ctxt->boids[0].position.z = cosf(r * 2.f); +		} +#if 0 +		printf("pos={%f,%f,%f},dir={%f,%f,%f},v=%f\n", +			b->position.x, b->position.y, b->position.z, +			b->direction.x, b->direction.y, b->direction.z, +			b->velocity); +#endif  	}  	/* characterize the current swarm */ @@ -236,37 +278,169 @@ static void swarm_update(swarm_context_t *ctxt, unsigned ticks)  } -static void swarm_render_fragment(void *context, unsigned ticks, unsigned cpu, til_fb_fragment_t *fragment) +static inline v2f_t swarm_project_point(swarm_context_t *ctxt, v3f_t *point)  { -	swarm_context_t	*ctxt = context; +	return (v2f_t) { +		.x = point->x / (point->z + SWARM_ZCONST + ctxt->ztweak), +		.y = point->y / (point->z + SWARM_ZCONST + ctxt->ztweak), +	}; +} -	swarm_update(ctxt, ticks); -	til_fb_fragment_zero(fragment); +static inline v2f_t swarm_scale(v2f_t normcoord, v2f_t scale) +{ +	return (v2f_t) { +		.x = normcoord.x * scale.x + scale.x, +		.y = normcoord.y * scale.y + scale.y, +	}; +} -	{ -		float		fw = fragment->frame_width, fh = fragment->frame_height; -		uint32_t	color = color_to_uint32(ctxt->color); -		fw *= .5f; -		fh *= .5f; +static inline v2f_t swarm_clip(v2f_t coord, til_fb_fragment_t *fragment) +{ +//	printf("coord={%f,%f}\n", coord.x, coord.y); -		for (unsigned i = 0; i < SWARM_SIZE; i++) { -			boid_t	*b = &ctxt->boids[i]; -			v2f_t	nc; +	return (v2f_t) { +		.x = coord.x < 0.f ? 0.f : (coord.x > (fragment->frame_width - 1) ? (fragment->frame_width - 1) : coord.x), +		.y = coord.y < 0.f ? 0.f : (coord.y > (fragment->frame_height - 1) ? (fragment->frame_height - 1) : coord.y), +	}; +} -			nc.x = b->position.x / (b->position.z + SWARM_ZCONST + ctxt->ztweak); -			nc.y = b->position.y / (b->position.z + SWARM_ZCONST + ctxt->ztweak); -			nc.x = nc.x * fw + fw; -			nc.y = nc.y * fh + fh; +static void swarm_draw_as_points(swarm_context_t *ctxt, til_fb_fragment_t *fragment) +{ +	v2f_t		scale = (v2f_t){ +				.x = fragment->frame_width * .5f, +				.y = fragment->frame_height * .5f, +			}; +	uint32_t	color = color_to_uint32(ctxt->color); -			til_fb_fragment_put_pixel_checked(fragment, nc.x, nc.y, color); +	for (unsigned i = 0; i < SWARM_SIZE; i++) { +		boid_t	*b = &ctxt->boids[i]; +		v2f_t	nc = swarm_scale(swarm_project_point(ctxt, &b->position), scale); + +		til_fb_fragment_put_pixel_checked(fragment, nc.x, nc.y, color); +	} +} + + +static void draw_line_unchecked(til_fb_fragment_t *fragment, int x1, int y1, int x2, int y2, uint32_t color) +{ +	int	x_delta = x2 - x1; +	int	y_delta = y2 - y1; +	int	sdx = x_delta < 0 ? -1 : 1; +	int	sdy = y_delta < 0 ? -1 : 1; + +	x_delta = abs(x_delta); +	y_delta = abs(y_delta); + +	if (x_delta >= y_delta) { +		/* X-major */ +		for (int minor = 0, x = 0; x <= x_delta; x++, x1 += sdx, minor += y_delta) { +			if (minor >= x_delta) { +				y1 += sdy; +				minor -= x_delta; +			} + +			til_fb_fragment_put_pixel_unchecked(fragment, x1, y1, color);  		} +	} else { +		/* Y-major */ +		for (int minor = 0, y = 0; y <= y_delta; y++, y1 += sdy, minor += x_delta) { +			if (minor >= y_delta) { +				x1 += sdx; +				minor -= y_delta; +			} + +			til_fb_fragment_put_pixel_unchecked(fragment, x1, y1, color); +		} +	} +} + + +static void swarm_draw_as_lines(swarm_context_t *ctxt, til_fb_fragment_t *fragment) +{ +	v2f_t		scale = (v2f_t){ +				.x = fragment->frame_width * .5f, +				.y = fragment->frame_height * .5f, +			}; +	uint32_t	color = color_to_uint32(ctxt->color); + +	/* this is similar to draw_as_points(), but derives two 3D points per boid, +	 * connecting them with a line in 2D. +	 */ +	for (unsigned i = 0; i < SWARM_SIZE; i++) { +		boid_t	*b = &ctxt->boids[i]; +		v3f_t	p1, p2; +		v2f_t	nc1, nc2; + +		p1 = v3f_add(b->position, v3f_mult_scalar(b->direction, b->velocity)); +		p2 = v3f_add(b->position, v3f_mult_scalar(v3f_invert(b->direction), b->velocity)); + +		/* don't bother drawing anything too close/behind the viewer, it +		 * just produces diagonal lines across the entire frame. +		 */ +		if (p1.z < -SWARM_ZCONST && p2.z < -SWARM_ZCONST) +			continue; + +		nc1 = swarm_clip(swarm_scale(swarm_project_point(ctxt, &p1), scale), fragment); +		nc2 = swarm_clip(swarm_scale(swarm_project_point(ctxt, &p2), scale), fragment); + +		draw_line_unchecked(fragment, nc1.x, nc1.y, nc2.x, nc2.y, color); +	} +} + + +static void swarm_render_fragment(void *context, unsigned ticks, unsigned cpu, til_fb_fragment_t *fragment) +{ +	swarm_context_t	*ctxt = context; + +	swarm_update(ctxt, ticks); + +	til_fb_fragment_zero(fragment); + +	switch (swarm_draw_style) { +	case SWARM_DRAW_STYLE_POINTS: +		return swarm_draw_as_points(ctxt, fragment); +	case SWARM_DRAW_STYLE_LINES: +		return swarm_draw_as_lines(ctxt, fragment);  	}  } +static int swarm_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc) +{ +	const char	*styles[] = { +				"points", +				"lines", +				NULL, +			}; +	const char	*style; +	int		r; + +	r = til_settings_get_and_describe_value(settings, +						&(til_setting_desc_t){ +							.name = "Particle drawing style", +							.key = "style", +							.values = styles, +							.preferred = styles[SWARM_DEFAULT_STYLE], +							.annotations = NULL +						}, +						&style, +						res_setting, +						res_desc); +	if (r) +		return r; + +	for (int i = 0; styles[i]; i++) { +		if (!strcmp(styles[i], style)) +			swarm_draw_style = i; +	} + +	return 0; +} + +  til_module_t	swarm_module = {  	.create_context = swarm_create_context,  	.destroy_context = swarm_destroy_context, @@ -274,4 +448,5 @@ til_module_t	swarm_module = {  	.name = "swarm",  	.description = "\"Boids\"-inspired particle swarm in 3D",  	.author = "Vito Caputo <vcaputo@pengaru.com>", +	.setup = swarm_setup,  }; | 
