From a1bcd46971f74e4fb6ac3b0242829601e390572f Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Mon, 25 Nov 2019 23:46:27 -0800 Subject: meta2d: add a classic 2D metaballs module --- src/Makefile.am | 2 +- src/modules/Makefile.am | 2 +- src/modules/meta2d/Makefile.am | 3 + src/modules/meta2d/meta2d.c | 235 +++++++++++++++++++++++++++ src/modules/meta2d/v2f.h | 352 ++++++++++++++++++++++++++++++++++++++++ src/modules/meta2d/v3f.h | 356 +++++++++++++++++++++++++++++++++++++++++ src/rototiller.c | 2 + 7 files changed, 950 insertions(+), 2 deletions(-) create mode 100644 src/modules/meta2d/Makefile.am create mode 100644 src/modules/meta2d/meta2d.c create mode 100644 src/modules/meta2d/v2f.h create mode 100644 src/modules/meta2d/v3f.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 2501c95..5446acd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,4 +4,4 @@ rototiller_SOURCES = fb.c fb.h fps.c fps.h rototiller.c rototiller.h sdl_fb.c se if ENABLE_DRM rototiller_SOURCES += drm_fb.c endif -rototiller_LDADD = modules/flui2d/libflui2d.a modules/julia/libjulia.a modules/montage/libmontage.a modules/pixbounce/libpixbounce.a modules/plasma/libplasma.a modules/ray/libray.a modules/roto/libroto.a modules/rtv/librtv.a modules/snow/libsnow.a modules/sparkler/libsparkler.a modules/stars/libstars.a modules/submit/libsubmit.a modules/swab/libswab.a libs/grid/libgrid.a libs/ray/libray.a libs/txt/libtxt.a libs/ascii/libascii.a libs/din/libdin.a -lm +rototiller_LDADD = modules/flui2d/libflui2d.a modules/julia/libjulia.a modules/meta2d/libmeta2d.a modules/montage/libmontage.a modules/pixbounce/libpixbounce.a modules/plasma/libplasma.a modules/ray/libray.a modules/roto/libroto.a modules/rtv/librtv.a modules/snow/libsnow.a modules/sparkler/libsparkler.a modules/stars/libstars.a modules/submit/libsubmit.a modules/swab/libswab.a libs/grid/libgrid.a libs/ray/libray.a libs/txt/libtxt.a libs/ascii/libascii.a libs/din/libdin.a -lm diff --git a/src/modules/Makefile.am b/src/modules/Makefile.am index 1622814..a4ff9e7 100644 --- a/src/modules/Makefile.am +++ b/src/modules/Makefile.am @@ -1 +1 @@ -SUBDIRS = flui2d julia montage pixbounce plasma ray roto rtv snow sparkler stars submit swab +SUBDIRS = flui2d julia meta2d montage pixbounce plasma ray roto rtv snow sparkler stars submit swab diff --git a/src/modules/meta2d/Makefile.am b/src/modules/meta2d/Makefile.am new file mode 100644 index 0000000..b1394d1 --- /dev/null +++ b/src/modules/meta2d/Makefile.am @@ -0,0 +1,3 @@ +noinst_LIBRARIES = libmeta2d.a +libmeta2d_a_SOURCES = meta2d.c v2f.h v3f.h +libmeta2d_a_CPPFLAGS = -I@top_srcdir@/src -I@top_srcdir@/src/libs diff --git a/src/modules/meta2d/meta2d.c b/src/modules/meta2d/meta2d.c new file mode 100644 index 0000000..08cd15a --- /dev/null +++ b/src/modules/meta2d/meta2d.c @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2019 - Vito Caputo - + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* https://en.wikipedia.org/wiki/Metaballs */ + +#include +#include + +#include "din/din.h" +#include "fb.h" +#include "rototiller.h" + +#include "v2f.h" +#include "v3f.h" + +#define META2D_NUM_BALLS 10 + +typedef struct meta2d_ball_t { + v2f_t position; + float radius; + v3f_t color; +} meta2d_ball_t; + +typedef struct meta2d_context_t { + unsigned n; + din_t *din_a, *din_b; + float din_t; + unsigned n_cpus; + meta2d_ball_t balls[META2D_NUM_BALLS]; +} meta2d_context_t; + + +/* convert a color into a packed, 32-bit rgb pixel value (taken from libs/ray/ray_color.h) */ +static inline uint32_t color_to_uint32(v3f_t color) { + uint32_t pixel; + + if (color.x > 1.0f) color.x = 1.0f; + if (color.y > 1.0f) color.y = 1.0f; + if (color.z > 1.0f) color.z = 1.0f; + + if (color.x < .0f) color.x = .0f; + if (color.y < .0f) color.y = .0f; + if (color.z < .0f) color.z = .0f; + + 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 * meta2d_create_context(unsigned num_cpus) +{ + meta2d_context_t *ctxt; + + ctxt = calloc(1, sizeof(meta2d_context_t)); + + /* perlin noise is used for some organic-ish random movement of the balls */ + ctxt->din_a = din_new(10, 10, META2D_NUM_BALLS + 2); + ctxt->din_b = din_new(10, 10, META2D_NUM_BALLS + 2); + + srand(getpid()); + ctxt->n_cpus = num_cpus; + + for (int i = 0; i < META2D_NUM_BALLS; i++) { + meta2d_ball_t *ball = &ctxt->balls[i]; + + v2f_rand(&ball->position, &(v2f_t){-.7f, -.7f}, &(v2f_t){.7f, .7f}); + ball->radius = rand() / (float)RAND_MAX * .2f + .05f; + v3f_rand(&ball->color, &(v3f_t){0.f, 0.f, 0.f}, &(v3f_t){1.f, 1.f, 1.f}); + } + + return ctxt; +} + + +static void meta2d_destroy_context(void *context) +{ + meta2d_context_t *ctxt = context; + + din_free(ctxt->din_a); + din_free(ctxt->din_b); + free(ctxt); +} + + +static int meta2d_fragmenter(void *context, const fb_fragment_t *fragment, unsigned number, fb_fragment_t *res_fragment) +{ + meta2d_context_t *ctxt = context; + + return fb_fragment_slice_single(fragment, ctxt->n_cpus, number, res_fragment); +} + + +static void meta2d_prepare_frame(void *context, unsigned n_cpus, fb_fragment_t *fragment, rototiller_fragmenter_t *res_fragmenter) +{ + meta2d_context_t *ctxt = context; + + *res_fragmenter = meta2d_fragmenter; + + /* move the balls around */ + for (int i = 0; i < META2D_NUM_BALLS; i++) { + meta2d_ball_t *ball = &ctxt->balls[i]; + float rad; + + /* Perlin noise indexed by position for x,y and i for z + * is used just for moving the metaballs around. + * + * Two noise fields are used with their values interpolated, + * starting with the din_a being 100% of the movement, + * with every frame migrating closer to din_b being 100%. + * + * Once din_b contributes 100%, it becomes din_a, and the old + * din_a becomes din_b which gets randomized, and the % resets + * to 0. + * + * This allows an organic continuous evolution of the field + * over time, at double the sampling cost since we're sampling + * two noise fields and interpolating them. Since this is + * just per-ball every frame, it's probably OK. Not like it's + * every pixel. + */ + + /* ad-hoc lerp of the two dins */ + rad = din(ctxt->din_a, &(v3f_t){ + .x = ball->position.x, + .y = ball->position.y, + .z = (float)i * (1.f / (float)META2D_NUM_BALLS)} + ) * (1.f - ctxt->din_t); + + rad += din(ctxt->din_b, &(v3f_t){ + .x = ball->position.x, + .y = ball->position.y, + .z = (float)i * (1.f / (float)META2D_NUM_BALLS)} + ) * ctxt->din_t; + + /* Perlin noise doesn't produce anything close to a uniform random distribution + * of -1..+1, so it can't just be mapped directly to 2*PI with all angles getting + * roughly equal occurrences in the long-run. For now I just *10.f and it seems + * to be OK. + */ + rad *= 10.f * 2.f * M_PI; + v2f_add(&ball->position, + &ball->position, + &(v2f_t){ + .x = cosf(rad) * .003f, /* small steps */ + .y = sinf(rad) * .003f, + }); + + v2f_clamp(&ball->position, + &ball->position, + &(v2f_t){-.8f, -.8f}, /* keep the balls mostly on-screen */ + &(v2f_t){.8f, .8f} + ); + } + + /* when din_t reaches 1 swap a<->b, reset din_t, randomize b */ + ctxt->din_t += .01f; + if (ctxt->din_t >= 1.f) { + din_t *tmp; + + tmp = ctxt->din_a; + ctxt->din_a = ctxt->din_b; + ctxt->din_b = tmp; + + din_randomize(ctxt->din_b); + ctxt->din_t = 0.f; + } +} + + +static void meta2d_render_fragment(void *context, unsigned cpu, fb_fragment_t *fragment) +{ + meta2d_context_t *ctxt = context; + float xf = 2.f / (float)fragment->frame_width; + float yf = 2.f / (float)fragment->frame_height; + v2f_t coord; + + for (int y = fragment->y; y < fragment->y + fragment->height; y++) { + coord.y = yf * (float)y - 1.f; + + for (int x = fragment->x; x < fragment->x + fragment->width; x++) { + v3f_t color = {}; + uint32_t pixel; + float t = 0; + + coord.x = xf * (float)x - 1.f; + + for (int i = 0; i < META2D_NUM_BALLS; i++) { + meta2d_ball_t *ball = &ctxt->balls[i]; + + float f; + + f = ball->radius * ball->radius / v2f_distance_sq(&coord, &ball->position); + v3f_add(&color, &color, v3f_mult_scalar(&(v3f_t){}, &ball->color, f)); + t += f; + } + + /* these thresholds define the thickness of the ribbon */ + if (t < .7f || t > .8f) + color = (v3f_t){}; + + pixel = color_to_uint32(color); + fb_fragment_put_pixel_unchecked(fragment, x, y, pixel); + } + } +} + + +rototiller_module_t meta2d_module = { + .create_context = meta2d_create_context, + .destroy_context = meta2d_destroy_context, + .prepare_frame = meta2d_prepare_frame, + .render_fragment = meta2d_render_fragment, + .name = "meta2d", + .description = "Classic 2D metaballs (threaded)", + .author = "Vito Caputo ", + .license = "GPLv3", +}; diff --git a/src/modules/meta2d/v2f.h b/src/modules/meta2d/v2f.h new file mode 100644 index 0000000..8f51ee0 --- /dev/null +++ b/src/modules/meta2d/v2f.h @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2018-2019 - Vito Caputo - + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * 2D vector operations header + * + * Variants prefixed with _ return a result vector struct by value. + * + * Variants missing the _ prefix for vector result operations return a pointer + * and must be either supplied the result memory as the first "res" argument + * which is then returned after being populated with the result's value, or + * NULL to allocate space for the result and that pointer is returned after being + * populated with its value. When supplying NULL result pointers the functions + * must allocate memory and thus may return NULL on OOM, so callers should + * check for NULL returns when supplying NULL for "res". + * + * Example: + * v2f_t foo, *foop; + * + * foo = _v2f_mult(&(v2f_t){1.f,1.f}, &(v2f_t){2.f,2.f}); + * + * is equivalent to: + * + * v2f_mult(&foo, &(v2f_t){1.f,1.f}, &(v2f_t){2.f,2.f}); + * + * or dynamically allocated: + * + * foop = v2f_mult(NULL, &(v2f_t){1.f,1.f}, &(v2f_t){2.f,2.f}); + * free(foop); + * + * is equivalent to: + * + * foop = malloc(sizeof(v2f_t)); + * v2f_mult(foop, &(v2f_t){1.f,1.f}, &(v2f_t){2.f,2.f}); + * free(foop); + */ + +#ifndef _V2F_H +#define _V2F_H + + +#include +#include + + +typedef struct v2f_t { + float x, y; +} v2f_t; + + +static inline v2f_t * _v2f_allocated(v2f_t **ptr) +{ + if (!*ptr) + *ptr = malloc(sizeof(v2f_t)); + + return *ptr; +} + + +static inline v2f_t _v2f_add(const v2f_t *a, const v2f_t *b) +{ + return (v2f_t){a->x + b->x, a->y + b->y}; +} + + +static inline v2f_t * v2f_add(v2f_t *res, const v2f_t *a, const v2f_t *b) +{ + if (_v2f_allocated(&res)) + *res = _v2f_add(a, b); + + return res; +} + + +static inline v2f_t _v2f_sub(const v2f_t *a, const v2f_t *b) +{ + return (v2f_t){a->x - b->x, a->y - b->y}; +} + + +static inline v2f_t * v2f_sub(v2f_t *res, const v2f_t *a, const v2f_t *b) +{ + if (_v2f_allocated(&res)) + *res = _v2f_sub(a, b); + + return res; +} + + +static inline v2f_t _v2f_mult(const v2f_t *a, const v2f_t *b) +{ + return (v2f_t){a->x * b->x, a->y * b->y}; +} + + +static inline v2f_t * v2f_mult(v2f_t *res, const v2f_t *a, const v2f_t *b) +{ + if (_v2f_allocated(&res)) + *res = _v2f_mult(a, b); + + return res; +} + + +static inline v2f_t _v2f_mult_scalar(const v2f_t *v, float scalar) +{ + return (v2f_t){ v->x * scalar, v->y * scalar }; +} + + +static inline v2f_t * v2f_mult_scalar(v2f_t *res, const v2f_t *v, float scalar) +{ + if (_v2f_allocated(&res)) + *res = _v2f_mult_scalar(v, scalar); + + return res; +} + + +static inline v2f_t _v2f_div_scalar(const v2f_t *v, float scalar) +{ + return _v2f_mult_scalar(v, 1.f / scalar); +} + + +static inline v2f_t * v2f_div_scalar(v2f_t *res, const v2f_t *v, float scalar) +{ + if (_v2f_allocated(&res)) + *res = _v2f_div_scalar(v, scalar); + + return res; +} + + +static inline float v2f_dot(const v2f_t *a, const v2f_t *b) +{ + return a->x * b->x + a->y * b->y; +} + + +static inline float v2f_length(const v2f_t *v) +{ + return sqrtf(v2f_dot(v, v)); +} + + +static inline float v2f_distance(const v2f_t *a, const v2f_t *b) +{ + return v2f_length(v2f_sub(&(v2f_t){}, a, b)); +} + + +static inline float v2f_distance_sq(const v2f_t *a, const v2f_t *b) +{ + v2f_t d = _v2f_sub(a, b); + + return v2f_dot(&d, &d); +} + + +static inline v2f_t _v2f_normalize(const v2f_t *v) +{ + return _v2f_mult_scalar(v, 1.0f / v2f_length(v)); +} + + +static inline v2f_t * v2f_normalize(v2f_t *res, const v2f_t *v) +{ + if (_v2f_allocated(&res)) + *res = _v2f_normalize(v); + + return res; +} + + +static inline v2f_t _v2f_lerp(const v2f_t *a, const v2f_t *b, float t) +{ + v2f_t lerp_a, lerp_b; + + lerp_a = _v2f_mult_scalar(a, 1.0f - t); + lerp_b = _v2f_mult_scalar(b, t); + + return _v2f_add(&lerp_a, &lerp_b); +} + + +static inline v2f_t * v2f_lerp(v2f_t *res, const v2f_t *a, const v2f_t *b, float t) +{ + if (_v2f_allocated(&res)) + *res = _v2f_lerp(a, b, t); + + return res; +} + + +static inline v2f_t _v2f_nlerp(const v2f_t *a, const v2f_t *b, float t) +{ + v2f_t lerp = _v2f_lerp(a, b, t); + + return _v2f_normalize(&lerp); +} + + +static inline v2f_t * v2f_nlerp(v2f_t *res, const v2f_t *a, const v2f_t *b, float t) +{ + if (_v2f_allocated(&res)) + *res = _v2f_nlerp(a, b, t); + + return res; +} + + +/* + * 1 ab-------bb + * | | | + * | | | + * | | | + * 0 aa-------ba + * t_x: 0---------1 + * ^ + * t_y + */ +static inline v2f_t _v2f_bilerp(const v2f_t *aa, const v2f_t *ab, const v2f_t *ba, const v2f_t *bb, float t_x, float t_y) +{ + v2f_t xa = _v2f_lerp(aa, ba, t_x); + v2f_t xb = _v2f_lerp(ab, bb, t_x); + + return _v2f_lerp(&xa, &xb, t_y); +} + + +static inline v2f_t * v2f_bilerp(v2f_t *res, const v2f_t *aa, const v2f_t *ab, const v2f_t *ba, const v2f_t *bb, float t_x, float t_y) +{ + if (_v2f_allocated(&res)) + *res = _v2f_bilerp(aa, ab, ba, bb, t_x, t_y); + + return res; +} + + +/* + * abb-------bbb + * /| /| + * aba-------bba| + * | | | | + * |aab------|bab + * |/ |/ + * aaa-------baa + */ +static inline v2f_t _v2f_trilerp(const v2f_t *aaa, const v2f_t *aba, const v2f_t *aab, const v2f_t *abb, const v2f_t *baa, const v2f_t *bba, const v2f_t *bab, const v2f_t *bbb, float t_x, float t_y, float t_z) +{ + v2f_t xya = _v2f_bilerp(aaa, aba, baa, bba, t_x, t_y); + v2f_t xyb = _v2f_bilerp(aab, abb, bab, bbb, t_x, t_y); + + return _v2f_lerp(&xya, &xyb, t_z); +} + + +static inline v2f_t * v2f_trilerp(v2f_t *res, const v2f_t *aaa, const v2f_t *aba, const v2f_t *aab, const v2f_t *abb, const v2f_t *baa, const v2f_t *bba, const v2f_t *bab, const v2f_t *bbb, float t_x, float t_y, float t_z) +{ + if (_v2f_allocated(&res)) + *res = _v2f_trilerp(aaa, aba, aab, abb, baa, bba, bab, bbb, t_x, t_y, t_z); + + return res; +} + + +static inline v2f_t _v2f_rand(const v2f_t *min, const v2f_t *max) +{ + return (v2f_t){ + .x = min->x + (float)rand() * (1.f/RAND_MAX) * (max->x - min->x), + .y = min->y + (float)rand() * (1.f/RAND_MAX) * (max->y - min->y), + }; +} + + +static inline v2f_t * v2f_rand(v2f_t *res, const v2f_t *min, const v2f_t *max) +{ + if (_v2f_allocated(&res)) + *res = _v2f_rand(min, max); + + return res; +} + + +static inline v2f_t _v2f_ceil(const v2f_t *v) +{ + return (v2f_t){ + .x = ceilf(v->x), + .y = ceilf(v->y), + }; +} + + +static inline v2f_t * v2f_ceil(v2f_t *res, const v2f_t *v) +{ + if (_v2f_allocated(&res)) + *res = _v2f_ceil(v); + + return res; +} + + +static inline v2f_t _v2f_floor(const v2f_t *v) +{ + return (v2f_t){ + .x = floorf(v->x), + .y = floorf(v->y), + }; +} + + +static inline v2f_t * v2f_floor(v2f_t *res, const v2f_t *v) +{ + if (_v2f_allocated(&res)) + *res = _v2f_floor(v); + + return res; +} + + +static inline v2f_t _v2f_clamp(const v2f_t *v, const v2f_t *min, const v2f_t *max) +{ + return (v2f_t){ + .x = v->x < min->x ? min->x : v->x > max->x ? max->x : v->x, + .y = v->y < min->y ? min->y : v->y > max->y ? max->y : v->y, + }; +} + + +static inline v2f_t * v2f_clamp(v2f_t *res, const v2f_t *v, const v2f_t *min, const v2f_t *max) +{ + if (_v2f_allocated(&res)) + *res = _v2f_clamp(v, min, max); + + return res; +} + +#endif diff --git a/src/modules/meta2d/v3f.h b/src/modules/meta2d/v3f.h new file mode 100644 index 0000000..79aea72 --- /dev/null +++ b/src/modules/meta2d/v3f.h @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2018-2019 - Vito Caputo - + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * 3D vector operations header + * + * Variants prefixed with _ return a result vector struct by value. + * + * Variants missing the _ prefix for vector result operations return a pointer + * and must be either supplied the result memory as the first "res" argument + * which is then returned after being populated with the result's value, or + * NULL to allocate space for the result and that pointer is returned after being + * populated with its value. When supplying NULL result pointers the functions + * must allocate memory and thus may return NULL on OOM, so callers should + * check for NULL returns when supplying NULL for "res". + * + * Example: + * v3f_t foo, *foop; + * + * foo = _v3f_mult(&(v3f_t){1.f,1.f,1.f}, &(v3f_t){2.f,2.f,2.f}); + * + * is equivalent to: + * + * v3f_mult(&foo, &(v3f_t){1.f,1.f,1.f}, &(v3f_t){2.f,2.f,2.f}); + * + * or dynamically allocated: + * + * foop = v3f_mult(NULL, &(v3f_t){1.f,1.f,1.f}, &(v3f_t){2.f,2.f,2.f}); + * free(foop); + * + * is equivalent to: + * + * foop = malloc(sizeof(v3f_t)); + * v3f_mult(foop, &(v3f_t){1.f,1.f,1.f}, &(v3f_t){2.f,2.f,2.f}); + * free(foop); + */ + +#ifndef _V3F_H +#define _V3F_H + + +#include +#include + + +typedef struct v3f_t { + float x, y, z; +} v3f_t; + + +static inline v3f_t * _v3f_allocated(v3f_t **ptr) +{ + if (!*ptr) + *ptr = malloc(sizeof(v3f_t)); + + return *ptr; +} + + +static inline v3f_t _v3f_add(const v3f_t *a, const v3f_t *b) +{ + return (v3f_t){a->x + b->x, a->y + b->y, a->z + b->z}; +} + + +static inline v3f_t * v3f_add(v3f_t *res, const v3f_t *a, const v3f_t *b) +{ + if (_v3f_allocated(&res)) + *res = _v3f_add(a, b); + + return res; +} + + +static inline v3f_t _v3f_sub(const v3f_t *a, const v3f_t *b) +{ + return (v3f_t){a->x - b->x, a->y - b->y, a->z - b->z}; +} + + +static inline v3f_t * v3f_sub(v3f_t *res, const v3f_t *a, const v3f_t *b) +{ + if (_v3f_allocated(&res)) + *res = _v3f_sub(a, b); + + return res; +} + + +static inline v3f_t _v3f_mult(const v3f_t *a, const v3f_t *b) +{ + return (v3f_t){a->x * b->x, a->y * b->y, a->z * b->z}; +} + + +static inline v3f_t * v3f_mult(v3f_t *res, const v3f_t *a, const v3f_t *b) +{ + if (_v3f_allocated(&res)) + *res = _v3f_mult(a, b); + + return res; +} + + +static inline v3f_t _v3f_mult_scalar(const v3f_t *v, float scalar) +{ + return (v3f_t){v->x * scalar, v->y * scalar, v->z * scalar}; +} + + +static inline v3f_t * v3f_mult_scalar(v3f_t *res, const v3f_t *v, float scalar) +{ + if (_v3f_allocated(&res)) + *res = _v3f_mult_scalar(v, scalar); + + return res; +} + + +static inline v3f_t _v3f_div_scalar(const v3f_t *v, float scalar) +{ + return _v3f_mult_scalar(v, 1.f / scalar); +} + + +static inline v3f_t * v3f_div_scalar(v3f_t *res, const v3f_t *v, float scalar) +{ + if (_v3f_allocated(&res)) + *res = _v3f_div_scalar(v, scalar); + + return res; +} + + +static inline float v3f_dot(const v3f_t *a, const v3f_t *b) +{ + return a->x * b->x + a->y * b->y + a->z * b->z; +} + + +static inline float v3f_length(const v3f_t *v) +{ + return sqrtf(v3f_dot(v, v)); +} + + +static inline float v3f_distance(const v3f_t *a, const v3f_t *b) +{ + return v3f_length(v3f_sub(&(v3f_t){}, a, b)); +} + + +static inline float v3f_distance_sq(const v3f_t *a, const v3f_t *b) +{ + v3f_t d = _v3f_sub(a, b); + + return v3f_dot(&d, &d); +} + + +static inline v3f_t _v3f_normalize(const v3f_t *v) +{ + return _v3f_mult_scalar(v, 1.0f / v3f_length(v)); +} + + +static inline v3f_t * v3f_normalize(v3f_t *res, const v3f_t *v) +{ + if (_v3f_allocated(&res)) + *res = _v3f_normalize(v); + + return res; +} + + +static inline v3f_t _v3f_lerp(const v3f_t *a, const v3f_t *b, float t) +{ + v3f_t lerp_a, lerp_b; + + lerp_a = _v3f_mult_scalar(a, 1.0f - t); + lerp_b = _v3f_mult_scalar(b, t); + + return _v3f_add(&lerp_a, &lerp_b); +} + + +static inline v3f_t * v3f_lerp(v3f_t *res, const v3f_t *a, const v3f_t *b, float t) +{ + if (_v3f_allocated(&res)) + *res = _v3f_lerp(a, b, t); + + return res; +} + + +static inline v3f_t _v3f_nlerp(const v3f_t *a, const v3f_t *b, float t) +{ + v3f_t lerp = _v3f_lerp(a, b, t); + + return _v3f_normalize(&lerp); +} + + +static inline v3f_t * v3f_nlerp(v3f_t *res, const v3f_t *a, const v3f_t *b, float t) +{ + if (_v3f_allocated(&res)) + *res = _v3f_nlerp(a, b, t); + + return res; +} + + +/* + * 1 ab-------bb + * | | | + * | | | + * | | | + * 0 aa-------ba + * t_x: 0---------1 + * ^ + * t_y + */ +static inline v3f_t _v3f_bilerp(const v3f_t *aa, const v3f_t *ab, const v3f_t *ba, const v3f_t *bb, float t_x, float t_y) +{ + v3f_t xa = _v3f_lerp(aa, ba, t_x); + v3f_t xb = _v3f_lerp(ab, bb, t_x); + + return _v3f_lerp(&xa, &xb, t_y); +} + + +static inline v3f_t * v3f_bilerp(v3f_t *res, const v3f_t *aa, const v3f_t *ab, const v3f_t *ba, const v3f_t *bb, float t_x, float t_y) +{ + if (_v3f_allocated(&res)) + *res = _v3f_bilerp(aa, ab, ba, bb, t_x, t_y); + + return res; +} + + +/* + * abb-------bbb + * /| /| + * aba-------bba| + * | | | | + * |aab------|bab + * |/ |/ + * aaa-------baa + */ +static inline v3f_t _v3f_trilerp(const v3f_t *aaa, const v3f_t *aba, const v3f_t *aab, const v3f_t *abb, const v3f_t *baa, const v3f_t *bba, const v3f_t *bab, const v3f_t *bbb, float t_x, float t_y, float t_z) +{ + v3f_t xya = _v3f_bilerp(aaa, aba, baa, bba, t_x, t_y); + v3f_t xyb = _v3f_bilerp(aab, abb, bab, bbb, t_x, t_y); + + return _v3f_lerp(&xya, &xyb, t_z); +} + + +static inline v3f_t * v3f_trilerp(v3f_t *res, const v3f_t *aaa, const v3f_t *aba, const v3f_t *aab, const v3f_t *abb, const v3f_t *baa, const v3f_t *bba, const v3f_t *bab, const v3f_t *bbb, float t_x, float t_y, float t_z) +{ + if (_v3f_allocated(&res)) + *res = _v3f_trilerp(aaa, aba, aab, abb, baa, bba, bab, bbb, t_x, t_y, t_z); + + return res; +} + + +static inline v3f_t _v3f_cross(const v3f_t *a, const v3f_t *b) +{ + return (v3f_t){ + .x = a->y * b->z - a->z * b->y, + .y = a->z * b->x - a->x * b->z, + .z = a->x * b->y - a->y * b->x, + }; +} + + +static inline v3f_t * v3f_cross(v3f_t *res, const v3f_t *a, const v3f_t *b) +{ + if (_v3f_allocated(&res)) + *res = _v3f_cross(a, b); + + return res; +} + + +static inline v3f_t _v3f_rand(const v3f_t *min, const v3f_t *max) +{ + return (v3f_t){ + .x = min->x + (float)rand() * (1.f/RAND_MAX) * (max->x - min->x), + .y = min->y + (float)rand() * (1.f/RAND_MAX) * (max->y - min->y), + .z = min->z + (float)rand() * (1.f/RAND_MAX) * (max->z - min->z), + }; +} + + +static inline v3f_t * v3f_rand(v3f_t *res, const v3f_t *min, const v3f_t *max) +{ + if (_v3f_allocated(&res)) + *res = _v3f_rand(min, max); + + return res; +} + + +static inline v3f_t _v3f_ceil(const v3f_t *v) +{ + return (v3f_t){ + .x = ceilf(v->x), + .y = ceilf(v->y), + .z = ceilf(v->z), + }; +} + + +static inline v3f_t * v3f_ceil(v3f_t *res, const v3f_t *v) +{ + if (_v3f_allocated(&res)) + *res = _v3f_ceil(v); + + return res; +} + + +static inline v3f_t _v3f_floor(const v3f_t *v) +{ + return (v3f_t){ + .x = floorf(v->x), + .y = floorf(v->y), + .z = floorf(v->z), + }; +} + + +static inline v3f_t * v3f_floor(v3f_t *res, const v3f_t *v) +{ + if (_v3f_allocated(&res)) + *res = _v3f_floor(v); + + return res; +} + +#endif diff --git a/src/rototiller.c b/src/rototiller.c index 42b9007..d8fa665 100644 --- a/src/rototiller.c +++ b/src/rototiller.c @@ -34,6 +34,7 @@ fb_ops_t *fb_ops; extern rototiller_module_t flui2d_module; extern rototiller_module_t julia_module; +extern rototiller_module_t meta2d_module; extern rototiller_module_t montage_module; extern rototiller_module_t pixbounce_module; extern rototiller_module_t plasma_module; @@ -49,6 +50,7 @@ extern rototiller_module_t swab_module; static const rototiller_module_t *modules[] = { &flui2d_module, &julia_module, + &meta2d_module, &montage_module, &pixbounce_module, &plasma_module, -- cgit v1.2.1