summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README77
-rw-r--r--src/modules/checkers/checkers.c9
-rw-r--r--src/modules/droste/droste.c4
-rw-r--r--src/modules/flow/flow.c47
-rw-r--r--src/modules/submit/submit.c87
5 files changed, 152 insertions, 72 deletions
diff --git a/README b/README
index 219e6bb..911a566 100644
--- a/README
+++ b/README
@@ -51,47 +51,54 @@ Quickly jump sections of this text by searching forward/reverse for ---
tool `rototiller` without any flags. This enters an interactive setup
mode, beginning with selecting the module to use from a list.
- Here's what's output as of writing this document:
+ Here's what's output as of last updating this document:
```
$ build/src/rototiller
/module:
Renderer module:
- 0: blinds: Retro 80s-inspired window blinds (threaded)
- 1: checkers: Checker-patterned overlay (threaded)
- 2: compose: Layered modules compositor
- 3: drizzle: Classic 2D rain effect (threaded (poorly))
- 4: flow: 3D flow field (threaded)
- 5: flui2d: Fluid dynamics simulation in 2D (threaded (poorly))
- 6: julia: Julia set fractal morpher (threaded)
- 7: meta2d: Classic 2D metaballs (threaded)
- 8: mixer: Module blender (threaded)
- 9: moire: 2D Moire interference patterns (threaded)
- 10: montage: Rototiller montage (threaded)
- 11: pixbounce: Pixmap bounce (threaded)
- 12: plasma: Oldskool plasma effect (threaded)
- 13: plato: Platonic solids rendered in 3D
- 14: ray: Ray tracer (threaded)
- 15: rkt: GNU Rocket module sequencer
- 16: roto: Anti-aliased tiled texture rotation (threaded)
- 17: rtv: Rototiller TV
- 18: shapes: Procedural 2D shapes (threaded)
- 19: snow: TV snow / white noise (threaded)
- 20: sparkler: Particle system with spatial interactions (threaded (poorly))
- 21: spiro: Spirograph emulator
- 22: stars: Basic starfield
- 23: strobe: Strobe light (threaded)
- 24: submit: Cellular automata conquest game sim (threaded (poorly))
- 25: swab: Colorful perlin-noise visualization (threaded)
- 26: swarm: "Boids"-inspired particle swarm in 3D
- 27: voronoi: Voronoi diagram (threaded)
- 28: blank: Blanker (built-in)
- 29: none: Disabled (built-in)
- 30: noop: Nothing-doer (built-in)
- 31: ref: Context referencer (built-in)
- Enter a value 0-31 [17 (rtv)]:
- ```
+ 0: book: Flipbook module
+ 1: blinds: Retro 80s-inspired window blinds (threaded)
+ 2: checkers: Checker-patterned overlay (threaded)
+ 3: compose: Layered modules compositor
+ 4: drizzle: Classic 2D rain effect (threaded (poorly))
+ 5: droste: Droste effect (threaded)
+ 6: flow: 3D flow field (threaded)
+ 7: flui2d: Fluid dynamics simulation in 2D (threaded (poorly))
+ 8: julia: Julia set fractal morpher (threaded)
+ 9: meta2d: Classic 2D metaballs (threaded)
+ 10: mixer: Module blender (threaded)
+ 11: moire: 2D Moire interference patterns (threaded)
+ 12: montage: Rototiller montage (threaded)
+ 13: pan: Simple panning effect (threaded)
+ 14: pixbounce: Pixmap bounce (threaded)
+ 15: plasma: Oldskool plasma effect (threaded)
+ 16: plato: Platonic solids rendered in 3D
+ 17: playit: .IT tracked music file player
+ 18: ray: Ray tracer (threaded)
+ 19: rkt: GNU Rocket module sequencer
+ 20: roto: Anti-aliased tiled texture rotation (threaded)
+ 21: rtv: Rototiller TV
+ 22: shapes: Procedural 2D shapes (threaded)
+ 23: snow: TV snow / white noise (threaded)
+ 24: sparkler: Particle system with spatial interactions (threaded (poorly))
+ 25: spiro: Spirograph emulator
+ 26: spokes: Twisted spokes
+ 27: stars: Basic starfield
+ 28: strobe: Strobe light (threaded)
+ 29: submit: Cellular automata conquest game sim (threaded (poorly))
+ 30: swab: Colorful perlin-noise visualization (threaded)
+ 31: swarm: "Boids"-inspired particle swarm in 3D
+ 32: voronoi: Voronoi diagram (threaded)
+ 33: asc: ASCII text (built-in)
+ 34: blank: Blanker (built-in)
+ 35: none: Disabled (built-in)
+ 36: noop: Nothing-doer (built-in)
+ 37: pre: Pre-render hook registration (built-in)
+ 38: ref: Context referencer (built-in)
+ Enter a value 0-38 [21 (rtv)]:
+ ```
Most modules are simple rendering modules, implementing a single
real-time graphics algorithm.
diff --git a/src/modules/checkers/checkers.c b/src/modules/checkers/checkers.c
index 59421c3..4fe3207 100644
--- a/src/modules/checkers/checkers.c
+++ b/src/modules/checkers/checkers.c
@@ -148,7 +148,7 @@ static til_module_context_t * checkers_create_context(const til_module_t *module
* adding a size member to til_module_context_t, which is straightforward to add.
*/
- ctxt->waste_fb = (til_fb_fragment_t){
+ ctxt->waste_fb = (til_fb_fragment_t){
.buf = malloc(waste_fb_size * waste_fb_size * sizeof(uint32_t)),
.frame_width = waste_fb_size,
.frame_height = waste_fb_size,
@@ -476,6 +476,13 @@ static int checkers_finish_frame(til_module_context_t *context, til_stream_t *st
* modules wanting to do the same thing with concurrent clones so it's worth sorting
* out all the details.
*/
+
+ /* It's important that the waste_fb's cleared state match the actual fragment_ptr's,
+ * since some modules do drastically different things when overlayed vs. not - which
+ * they detect via the cleared flag.
+ */
+ ctxt->waste_fb.cleared = (*fragment_ptr)->cleared;
+
til_module_render(ctxt->fill_module_contexts[i], stream, ticks, &waste_fb_ptr);
#if 0
/* This is probably an interesting thing to measure. On a busy system, it's not surprising
diff --git a/src/modules/droste/droste.c b/src/modules/droste/droste.c
index 9cbfbac..717e513 100644
--- a/src/modules/droste/droste.c
+++ b/src/modules/droste/droste.c
@@ -112,6 +112,10 @@ static int droste_fragmenter(til_module_context_t *context, const til_fb_fragmen
assert(fragment);
assert(res_fragment);
+ /* This should be using half of the frame_{width,height}, but must be clipped
+ * to the fragment dimensions. As-is this is quite broken in scenarios like droste in a checkers filler
+ * when the perimeter checkers get clipped. FIXME FIXME FIXME
+ */
inset.width = fragment->width >> 1;
inset.height = fragment->height >> 1;
inset.frame_width = inset.width;
diff --git a/src/modules/flow/flow.c b/src/modules/flow/flow.c
index c9b94e4..d28f9f3 100644
--- a/src/modules/flow/flow.c
+++ b/src/modules/flow/flow.c
@@ -35,6 +35,17 @@ typedef struct flow_element_t {
v3f_t position_a, position_b;
v3f_t velocity; /* per-iter step + direction applicable directly to position_a */
v3f_t color;
+
+ /* In the first pass we visit each element just once for the simulation, while there
+ * the particles are projected into the 2d fragment space with those coordinates cached
+ * in the element. Then the second pass accesses these to determine if the particle is
+ * in the fragment being drawn. As-is the second pass visits all elements since they're
+ * not (yet) kept organized spatially.
+ */
+ struct {
+ unsigned x1, y1;
+ unsigned x2, y2;
+ } fragspace;
} flow_element_t;
typedef struct flow_context_t {
@@ -83,7 +94,7 @@ static void flow_ff_populator(void *context, unsigned size, const ff_data_t *oth
v3f_t c = v3f_rand(seedp, 0.f, 1.0f);
size_t idx = x * size * size + y * size + z;
- field[idx].direction = v3f_lerp(&other[idx].direction, &v, .75f);
+ field[idx].direction = v3f_lerp(&other[idx].direction, &v, .50f);
field[idx].color = v3f_lerp(&other[idx].color, &c, .75f);
}
}
@@ -219,6 +230,8 @@ static void flow_render_fragment(til_module_context_t *context, til_stream_t *st
flow_element_t *e = &ctxt->elements[fragment->number * ctxt->n_elements_per_cpu];
unsigned n = ctxt->n_elements_per_cpu;
float w = ctxt->w * .5f + .5f;
+ unsigned ffw = fragment->frame_width,
+ ffh = fragment->frame_height;
/* XXX: note the fragment->number is used above as the cpu number, this is to ensure all cpu #s
* are actually used. Since our noop_fragmenter_per_cpu always produces a fragment per cpu,
@@ -261,6 +274,15 @@ static void flow_render_fragment(til_module_context_t *context, til_stream_t *st
*/
d.direction = v3f_mult_scalar(&d.direction, (float)ctxt->n_iters);
e->position_b = v3f_add(&pos, &d.direction);
+
+ /* Now do the 2D projection part and store those results in the element, where
+ * it can be quickly checked by the second pass.
+ */
+#define ZCONST 1.0f
+ e->fragspace.x1 = pos.x / (pos.z + ZCONST) * ffw + (ffw >> 1);
+ e->fragspace.y1 = pos.y / (pos.z + ZCONST) * ffh + (ffh >> 1) ;
+ e->fragspace.x2 = e->position_b.x / (e->position_b.z + ZCONST) * ffw + (ffw >> 1);
+ e->fragspace.y2 = e->position_b.y / (e->position_b.z + ZCONST) * ffh + (ffh >> 1) ;
}
return;
@@ -281,20 +303,16 @@ static void flow_render_fragment(til_module_context_t *context, til_stream_t *st
flow_element_t *e = &ctxt->elements[i];
v3f_t pos = e->position_a;
v3f_t v = e->velocity;
- unsigned x1, y1, x2, y2;
+ unsigned x1 = e->fragspace.x1,
+ y1 = e->fragspace.y1,
+ x2 = e->fragspace.x2,
+ y2 = e->fragspace.y2;
uint32_t pixel;
- /* Perspective-project the endpoints of the element's travel, this is
- * the part we can't currently avoid doing per-element per-fragment.
- */
-#define ZCONST 1.0f
- x1 = pos.x / (pos.z + ZCONST) * ffw + (ffw >> 1);
- y1 = pos.y / (pos.z + ZCONST) * ffh + (ffh >> 1) ;
- x2 = e->position_b.x / (e->position_b.z + ZCONST) * ffw + (ffw >> 1);
- y2 = e->position_b.y / (e->position_b.z + ZCONST) * ffh + (ffh >> 1) ;
-
/* for cases obviously outside the fragment, don't draw anything */
+ /* FIXME: these early-outs don't consider the ctxt->n_iters interpolated coordinates */
+
/* totally outside (above) */
if (y1 < fy1 && y2 < fy1)
continue;
@@ -324,6 +342,10 @@ static void flow_render_fragment(til_module_context_t *context, til_stream_t *st
if (!ctxt->n_iters)
continue;
+ /* TODO: why isn't this a simple line draw of (x1,y1)..(x2,y2)?
+ * that would eliminate the cumulative error potential for going out of bounds,
+ * which is the real reason put_pixel_unchecked() can't be used in this loop.
+ */
for (unsigned j = 1; j < ctxt->n_iters - 1; j++) {
pos = v3f_add(&pos, &v);
@@ -331,7 +353,8 @@ static void flow_render_fragment(til_module_context_t *context, til_stream_t *st
x1 = pos.x / (pos.z + ZCONST) * ffw + (ffw >> 1);
y1 = pos.y / (pos.z + ZCONST) * ffh + (ffh >> 1);
- (void) til_fb_fragment_put_pixel_unchecked(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, x1, y1, pixel);
+ /* XXX: now that [xy]1 are changed, unchecked can't be used, it could be done more cleverly */
+ (void) til_fb_fragment_put_pixel_checked(fragment, TIL_FB_DRAW_FLAG_TEXTURABLE, x1, y1, pixel);
}
continue;
diff --git a/src/modules/submit/submit.c b/src/modules/submit/submit.c
index fea9444..415f795 100644
--- a/src/modules/submit/submit.c
+++ b/src/modules/submit/submit.c
@@ -33,19 +33,19 @@
#define TICKS_PER_FRAME 8000
typedef struct color_t {
- float r, g, b;
+ float r, g, b, a;
} color_t;
static color_t colors[NUM_PLAYERS + 1] = {
- {}, /* uninitialized cell starts black, becomes winner colors */
- {1.f, .317f, 0.f }, /* orange */
- {.627f, .125f, 1.f }, /* blue */
- {.878f, 0.f, 0.f }, /* red */
- {.165f, .843f, .149f }, /* green */
- {0.f, .878f, .815f }, /* cyan */
- {.878f, 0.f, 1.f }, /* purple */
- {.906f, .937f, 0.f }, /* yellow */
- {}, /* black */
+ {}, /* uninitialized cell starts black, becomes winner colors */
+ {1.f, .317f, 0.f, 1.f }, /* orange */
+ {.627f, .125f, 1.f, 1.f }, /* blue */
+ {.878f, 0.f, 0.f, 1.f }, /* red */
+ {.165f, .843f, .149f, 1.f }, /* green */
+ {0.f, .878f, .815f, 1.f }, /* cyan */
+ {.878f, 0.f, 1.f, 1.f }, /* purple */
+ {.906f, .937f, 0.f, 1.f }, /* yellow */
+ {}, /* black */
};
@@ -71,8 +71,11 @@ static inline uint32_t color_to_uint32(color_t color) {
if (color.r > 1.0f) color.r = 1.0f;
if (color.g > 1.0f) color.g = 1.0f;
if (color.b > 1.0f) color.b = 1.0f;
+ if (color.a > 1.0f) color.a = 1.0f;
- pixel = (uint32_t)(color.r * 255.0f);
+ pixel = (uint32_t)(color.a * 255.0f);
+ pixel <<= 8;
+ pixel |= (uint32_t)(color.r * 255.0f);
pixel <<= 8;
pixel |= (uint32_t)(color.g * 255.0f);
pixel <<= 8;
@@ -108,6 +111,7 @@ static color_t color_lerp(color_t *a, color_t *b, float t)
.r = a->r * (1.f - t) + b->r * t,
.g = a->g * (1.f - t) + b->g * t,
.b = a->b * (1.f - t) + b->b * t,
+ .a = a->a * (1.f - t) + b->a * t,
};
return res;
@@ -194,14 +198,28 @@ static void draw_grid(submit_context_t *ctxt, til_fb_fragment_t *fragment)
float xscale = ((float)GRID_SIZE - 1.f) / (float)fragment->frame_width;
float yscale = ((float)GRID_SIZE - 1.f) / (float)fragment->frame_height;
- for (int y = 0; y < fragment->height; y++) {
- for (int x = 0; x < fragment->width; x++) {
- uint32_t color;
+ if (!fragment->cleared) {
+ for (int y = 0; y < fragment->height; y++) {
+ for (int x = 0; x < fragment->width; x++) {
+ uint32_t color;
- /* TODO: this could be optimized a bit! i.e. don't recompute the y for every x etc. */
- color = sample_grid(ctxt, .5f + ((float)(fragment->x + x)) * xscale, .5f + ((float)(fragment->y + y)) * yscale);
- til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, color);
+ /* TODO: this could be optimized a bit! i.e. don't recompute the y for every x etc. */
+ color = sample_grid(ctxt, .5f + ((float)(fragment->x + x)) * xscale, .5f + ((float)(fragment->y + y)) * yscale);
+ til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, color);
+ }
+ }
+ } else {
+ for (int y = 0; y < fragment->height; y++) {
+ for (int x = 0; x < fragment->width; x++) {
+ uint32_t color;
+
+ /* TODO: this could be optimized a bit! i.e. don't recompute the y for every x etc. */
+ color = sample_grid(ctxt, .5f + ((float)(fragment->x + x)) * xscale, .5f + ((float)(fragment->y + y)) * yscale);
+ if ((color & 0xff000000) == 0xff000000)
+ til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, color);
+ }
}
+
}
}
@@ -211,13 +229,26 @@ static void draw_grid_bilerp(submit_context_t *ctxt, til_fb_fragment_t *fragment
float xscale = ((float)GRID_SIZE - 2.f) / (float)fragment->frame_width;
float yscale = ((float)GRID_SIZE - 2.f) / (float)fragment->frame_height;
- for (int y = 0; y < fragment->height; y++) {
- for (int x = 0; x < fragment->width; x++) {
- uint32_t color;
+ if (!fragment->cleared) {
+ for (int y = 0; y < fragment->height; y++) {
+ for (int x = 0; x < fragment->width; x++) {
+ uint32_t color;
- /* TODO: this could be optimized a bit! i.e. don't recompute the y for every x etc. */
- color = sample_grid_bilerp(ctxt, 1.f + ((float)(fragment->x + x)) * xscale, 1.f + ((float)(fragment->y + y)) * yscale);
- til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, color);
+ /* TODO: this could be optimized a bit! i.e. don't recompute the y for every x etc. */
+ color = sample_grid_bilerp(ctxt, 1.f + ((float)(fragment->x + x)) * xscale, 1.f + ((float)(fragment->y + y)) * yscale);
+ til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, color);
+ }
+ }
+ } else {
+ for (int y = 0; y < fragment->height; y++) {
+ for (int x = 0; x < fragment->width; x++) {
+ uint32_t color;
+
+ /* TODO: this could be optimized a bit! i.e. don't recompute the y for every x etc. */
+ color = sample_grid_bilerp(ctxt, 1.f + ((float)(fragment->x + x)) * xscale, 1.f + ((float)(fragment->y + y)) * yscale);
+ if ((color & 0xff000000) == 0xff000000)
+ til_fb_fragment_put_pixel_unchecked(fragment, 0, fragment->x + x, fragment->y + y, color);
+ }
}
}
}
@@ -292,15 +323,22 @@ static void submit_destroy_context(til_module_context_t *context)
static void submit_prepare_frame(til_module_context_t *context, til_stream_t *stream, unsigned ticks, til_fb_fragment_t **fragment_ptr, til_frame_plan_t *res_frame_plan)
{
submit_context_t *ctxt = (submit_context_t *)context;
+ til_fb_fragment_t *fragment = *fragment_ptr;
*res_frame_plan = (til_frame_plan_t){ .fragmenter = til_fragmenter_tile64 };
+ if (ticks == context->last_ticks)
+ return;
+
if (ctxt->game_winner)
setup_grid(ctxt);
for (int i = 0; i < NUM_PLAYERS; i++) {
int moves = rand_r(&ctxt->til_module_context.seed) % TICKS_PER_FRAME;
+ if (fragment->cleared && colors[i + 1].a == 0.f)
+ moves = MIN(TICKS_PER_FRAME, moves + (TICKS_PER_FRAME / 10));
+
for (int j = 0; j < moves; j++)
grid_player_plan(ctxt->players[i], ctxt->seq++,
rand_r(&ctxt->til_module_context.seed) % GRID_SIZE,
@@ -332,10 +370,11 @@ til_module_t submit_module = {
.destroy_context = submit_destroy_context,
.prepare_frame = submit_prepare_frame,
.render_fragment = submit_render_fragment,
+ .setup = submit_setup,
.name = "submit",
.description = "Cellular automata conquest game sim (threaded (poorly))",
.author = "Vito Caputo <vcaputo@pengaru.com>",
- .setup = submit_setup,
+ .flags = TIL_MODULE_OVERLAYABLE,
};
© All Rights Reserved