summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/modules/swarm/swarm.c219
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,
};
© All Rights Reserved