summaryrefslogtreecommitdiff
path: root/src/modules/flow/flow.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/flow/flow.c')
-rw-r--r--src/modules/flow/flow.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/src/modules/flow/flow.c b/src/modules/flow/flow.c
new file mode 100644
index 0000000..636458f
--- /dev/null
+++ b/src/modules/flow/flow.c
@@ -0,0 +1,318 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "til.h"
+#include "til_fb.h"
+#include "til_module_context.h"
+#include "til_settings.h"
+
+#include "ff.h"
+#include "v3f.h"
+
+/* Copyright (C) 2017 Vito Caputo <vcaputo@pengaru.com> */
+
+/* TODO:
+ * - make threaded
+ * - make colorful
+ */
+
+#define FLOW_DEFAULT_SIZE "8"
+#define FLOW_DEFAULT_COUNT "30000"
+#define FLOW_DEFAULT_SPEED ".2"
+
+#define FLOW_MAX_SPEED 40
+
+typedef struct flow_element_t {
+ float lifetime;
+ v3f_t position;
+} flow_element_t;
+
+typedef struct flow_context_t {
+ til_module_context_t til_module_context;
+ ff_t *ff;
+ unsigned last_populate_idx;
+ unsigned n_iters;
+ unsigned n_elements;
+ flow_element_t elements[];
+} flow_context_t;
+
+typedef struct flow_setup_t {
+ til_setup_t til_setup;
+
+ unsigned size;
+ unsigned count;
+ float speed;
+} flow_setup_t;
+
+
+static void populator(void *context, unsigned size, const v3f_t *other, v3f_t *field)
+{
+ flow_context_t *ctxt = context;
+ unsigned *seedp = &ctxt->til_module_context.seed;
+ unsigned x, y, z;
+
+ for (x = 0; x < size; x++) {
+ for (y = 0; y < size; y++) {
+ for (z = 0; z < size; z++) {
+ v3f_t v = v3f_rand(seedp, -1.0f, 1.0f);
+ size_t idx = x * size * size + y * size + z;
+
+ field[idx] = v3f_lerp(&other[idx], &v, .75f);
+ }
+ }
+ }
+}
+
+
+static inline float rand_within_range(unsigned *seed, float min, float max)
+{
+ return (min + ((float)rand_r(seed) * (1.0f/RAND_MAX)) * (max - min));
+}
+
+
+static inline flow_element_t rand_element(unsigned *seed)
+{
+ flow_element_t e;
+
+ e.lifetime = rand_within_range(seed, 0.5f, 20.0f);
+ e.position = v3f_rand(seed, 0.0f, 1.0f);
+
+ return e;
+}
+
+
+static til_module_context_t * flow_create_context(const til_module_t *module, til_stream_t *stream, unsigned seed, unsigned ticks, unsigned n_cpus, til_setup_t *setup)
+{
+ flow_setup_t *s = (flow_setup_t *)setup;
+ flow_context_t *ctxt;
+ unsigned i;
+
+ ctxt = til_module_context_new(module, sizeof(flow_context_t) + sizeof(ctxt->elements[0]) * s->count, stream, seed, ticks, n_cpus, setup);
+ if (!ctxt)
+ return NULL;
+
+ ctxt->ff = ff_new(s->size, populator, ctxt);
+ if (!ctxt->ff)
+ return til_module_context_free(&ctxt->til_module_context);
+
+ for (i = 0; i < s->count; i++)
+ ctxt->elements[i] = rand_element(&ctxt->til_module_context.seed);
+
+ ctxt->n_iters = ceilf(s->speed * FLOW_MAX_SPEED);
+ ctxt->n_elements = s->count;
+
+ return &ctxt->til_module_context;
+}
+
+
+static void flow_destroy_context(til_module_context_t *context)
+{
+ flow_context_t *ctxt = (flow_context_t *)context;
+
+ ff_free(ctxt->ff);
+ free(context);
+}
+
+
+static inline uint32_t color_to_uint32_rgb(v3f_t color) {
+ uint32_t pixel;
+
+ /* doing this all per-pixel, ugh. */
+
+ color = v3f_clamp_scalar(0.0f, 1.0f, &color);
+
+ pixel = (uint32_t)(color.x * 255.0f);
+ pixel <<= 8;
+ pixel |= (uint32_t)(color.y * 255.0f);
+ pixel <<= 8;
+ pixel |= (uint32_t)(color.z * 255.0f);
+
+ return pixel;
+}
+
+
+static void flow_render_fragment(til_module_context_t *context, til_stream_t *stream, unsigned ticks, unsigned cpu, til_fb_fragment_t **fragment_ptr)
+{
+ flow_context_t *ctxt = (flow_context_t *)context;
+ til_fb_fragment_t *fragment = *fragment_ptr;
+ float w;
+
+ til_fb_fragment_clear(fragment);
+
+ w = (M_2_PI * asinf(fabsf(sinf((ticks * .001f))))) * 2.f - 1.f;
+ /* ^^ this approximates a triangle wave,
+ * a sine wave dwells too long for the illusion of continuously evolving
+ */
+
+ for (unsigned j = 0; j < ctxt->n_elements; j++) {
+ flow_element_t *e = &ctxt->elements[j];
+ v3f_t pos = e->position;
+ v3f_t v = ff_get(ctxt->ff, &pos, w * .5f + .5f);
+
+ v = v3f_mult_scalar(&v, .001f);
+
+ for (unsigned k = 0; k < ctxt->n_iters; k++) {
+ unsigned x, y;
+ v3f_t color;
+
+ pos = v3f_add(&pos, &v);
+#define ZCONST 1.0f
+ x = (pos.x * 2.f - 1.f) / (pos.z + ZCONST) * fragment->width + (fragment->width >> 1);
+ y = (pos.y * 2.f - 1.f) / (pos.z + ZCONST) * fragment->height + (fragment->height >> 1) ;
+
+ color.x = color.y = color.z = e->lifetime;
+
+ if (!til_fb_fragment_put_pixel_checked(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, x, y, color_to_uint32_rgb(color)) ||
+ pos.x < 0.f || pos.x > 1.f ||
+ pos.y < 0.f || pos.y > 1.f ||
+ pos.z < 0.f || pos.z > 1.f)
+ *e = rand_element(&ctxt->til_module_context.seed);
+ else
+ e->position = pos;
+ }
+
+ e->lifetime -= .1f;
+ if (e->lifetime <= 0.0f)
+ *e = rand_element(&ctxt->til_module_context.seed);
+ }
+
+ /* Re-populate the other field before changing directions.
+ * note if the frame rate is too low and we miss a >.95 sample
+ * this will regress to just revisiting the previous field which
+ * is relatively harmless.
+ */
+ if (fabsf(w) > .95f) {
+ unsigned other_idx;
+
+ other_idx = rintf(-w * .5f + .5f);
+ if (other_idx != ctxt->last_populate_idx) {
+ ff_populate(ctxt->ff, other_idx);
+ ctxt->last_populate_idx = other_idx;
+ }
+ }
+}
+
+
+static int flow_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup);
+
+
+til_module_t flow_module = {
+ .create_context = flow_create_context,
+ .destroy_context = flow_destroy_context,
+ .render_fragment = flow_render_fragment,
+ .setup = flow_setup,
+ .name = "flow",
+ .description = "3D flow field",
+ .author = "Vito Caputo <vcaputo@pengaru.com>",
+ .flags = TIL_MODULE_OVERLAYABLE,
+};
+
+
+static int flow_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup)
+{
+ til_setting_t *size;
+ const char *size_values[] = {
+ "2",
+ "4",
+ "8",
+ "16",
+ "32",
+ NULL
+ };
+ til_setting_t *count;
+ const char *count_values[] = {
+ "100",
+ "1000",
+ "5000",
+ "10000",
+ "20000",
+ "30000",
+ "40000",
+ "50000",
+ NULL
+ };
+ til_setting_t *speed;
+ const char *speed_values[] = {
+ ".02",
+ ".04",
+ ".08",
+ ".16",
+ ".2",
+ ".4",
+ ".6",
+ ".8",
+ ".9",
+ "1",
+ NULL
+ };
+ int r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Size of flow field cube",
+ .key = "size",
+ .regex = "\\[0-9]+", /* FIXME */
+ .preferred = FLOW_DEFAULT_SIZE,
+ .values = size_values,
+ .annotations = NULL
+ },
+ &size,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Count of flowing elements",
+ .key = "count",
+ .regex = "\\[0-9]+", /* FIXME */
+ .preferred = FLOW_DEFAULT_COUNT,
+ .values = count_values,
+ .annotations = NULL
+ },
+ &count,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ r = til_settings_get_and_describe_setting(settings,
+ &(til_setting_spec_t){
+ .name = "Speed of all flow through field",
+ .key = "speed",
+ .regex = "\\.[0-9]+", /* FIXME */
+ .preferred = FLOW_DEFAULT_SPEED,
+ .values = speed_values,
+ .annotations = NULL
+ },
+ &speed,
+ res_setting,
+ res_desc);
+ if (r)
+ return r;
+
+ if (res_setup) {
+ flow_setup_t *setup;
+
+ setup = til_setup_new(settings, sizeof(*setup), NULL, &flow_module);
+ if (!setup)
+ return -ENOMEM;
+
+ if (sscanf(size->value, "%u", &setup->size) != 1)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, size, res_setting, -EINVAL);
+
+ if (sscanf(count->value, "%u", &setup->count) != 1)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, count, res_setting, -EINVAL);
+
+ if (sscanf(speed->value, "%f", &setup->speed) != 1)
+ return til_setup_free_with_failed_setting_ret_err(&setup->til_setup, speed, res_setting, -EINVAL);
+
+ *res_setup = &setup->til_setup;
+ }
+
+ return 0;
+}
© All Rights Reserved