From 3b52c25b520e0e1b9143cafaee2670e57c89ef0d Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Sun, 25 Mar 2018 22:02:45 +0300 Subject: [PATCH] separated the smoke-text effect to reuse it in multiple parts if necessary --- src/greets.c | 241 +-------------------------- src/noise.c | 505 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/noise.h | 35 ++++ src/smoketxt.c | 371 +++++++++++++++++++++++++++++++++++++++++ src/smoketxt.h | 21 +++ 5 files changed, 939 insertions(+), 234 deletions(-) create mode 100644 src/noise.c create mode 100644 src/noise.h create mode 100644 src/smoketxt.c create mode 100644 src/smoketxt.h diff --git a/src/greets.c b/src/greets.c index 353d29c..8b128c6 100644 --- a/src/greets.c +++ b/src/greets.c @@ -11,6 +11,7 @@ #include "util.h" #include "gfxutil.h" #include "timer.h" +#include "smoketxt.h" #ifdef MSDOS #include "dos/gfx.h" /* for wait_vsync assembly macro */ @@ -18,66 +19,14 @@ void wait_vsync(void); #endif -/* if defined, use bilinear interpolation for dispersion field vectors */ -#define BILERP_FIELD -/* if defined randomize field vectors by RAND_FIELD_MAX */ -#define RANDOMIZE_FIELD - -#define RAND_FIELD_MAX 0.7 - #define BLUR_RAD 5 -#define PCOUNT 4000 -#define MAX_LIFE 7.0f -#define PALPHA 1.0f -#define ZBIAS 0.25 -#define DRAG 0.95 -#define FORCE 0.07 -#define FREQ 0.085 -static float wind[] = {-0.0, 0.0, 0.01}; - - -struct vec2 { - float x, y; -}; - -struct vec3 { - float x, y, z; -}; - -struct particle { - float x, y, z; - float vx, vy, vz; /* velocity */ - int r, g, b; - float life; -}; - -struct emitter { - struct particle *plist; - int pcount; -}; - -struct vfield { - struct vec2 pos, size; - - int width, height; - int xshift; - struct vec2 *v; -}; - static int init(void); static void destroy(void); static void start(long trans_time); static void draw(void); -int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz); -void update_particles(struct emitter *em, float dt); -void draw_particles(struct emitter *em); - -int init_vfield_load(struct vfield *vf, const char *fname); -void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir); - static struct screen scr = { "greets", @@ -87,11 +36,10 @@ static struct screen scr = { draw }; -static struct emitter em; -static struct vfield vfield; -static struct g3d_vertex *varr; static long start_time; +static struct smktxt *stx; + static uint16_t *cur_smokebuf, *prev_smokebuf; static int smokebuf_size; #define smokebuf_start (cur_smokebuf < prev_smokebuf ? cur_smokebuf : prev_smokebuf) @@ -113,23 +61,7 @@ struct screen *greets_screen(void) static int init(void) { - int xsz, ysz; - unsigned char *pixels; - - if(!(pixels = img_load_pixels("data/greets1.png", &xsz, &ysz, IMG_FMT_GREY8))) { - fprintf(stderr, "failed to load particle spawn map\n"); - return -1; - } - - init_emitter(&em, PCOUNT, pixels, xsz, ysz); - img_free_pixels(pixels); - - if(!(varr = malloc(PCOUNT * sizeof *varr))) { - perror("failed to allocate particle vertex buffer\n"); - return -1; - } - - if(init_vfield_load(&vfield, "data/vfield1") == -1) { + if(!(stx = create_smktxt("data/greets1.png", "data/vfield1"))) { return -1; } @@ -145,9 +77,8 @@ static int init(void) static void destroy(void) { - free(varr); - free(vfield.v); free(smokebuf_start); + destroy_smktxt(stx); } static void start(long trans_time) @@ -196,7 +127,7 @@ static void update(void) prev_my = mouse_y; prev_bmask = mouse_bmask; - update_particles(&em, dt); + update_smktxt(stx, dt); } static void draw(void) @@ -220,7 +151,7 @@ static void draw(void) memcpy(cur_smokebuf, prev_smokebuf, smokebuf_size); g3d_framebuffer(fb_width, fb_height, cur_smokebuf); - draw_particles(&em); + draw_smktxt(stx); g3d_framebuffer(fb_width, fb_height, fb_pixels); dest = fb_pixels; @@ -251,161 +182,3 @@ static void draw(void) swap_buffers(fb_pixels); last_swap = get_msec(); } - - - -int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz) -{ - int i, x, y; - float aspect = (float)xsz / (float)ysz; - struct particle *p; - - if(!(em->plist = malloc(num * sizeof *em->plist))) { - return -1; - } - em->pcount = num; - - p = em->plist; - for(i=0; ix = (float)x / (float)xsz - 0.5; - p->y = -(float)y / (float)xsz + 0.5 / aspect; - p->z = ((float)i / (float)num * 2.0 - 1.0) * 0.005; - p->r = p->g = p->b = 255; - p->vx = p->vy = p->vz = 0.0f; - p->life = MAX_LIFE; - ++p; - } - return 0; -} - -void update_particles(struct emitter *em, float dt) -{ - int i; - struct vec2 accel; - struct particle *p = em->plist; - struct g3d_vertex *v = varr; - - for(i=0; ipcount; i++) { - vfield_eval(&vfield, p->x, p->y, &accel); - p->x += p->vx * DRAG * dt; - p->y += p->vy * DRAG * dt; - p->z += p->vz * DRAG * dt; - p->vx += (wind[0] + accel.x * FORCE) * dt; - p->vy += (wind[1] + accel.y * FORCE) * dt; - p->vz += (wind[2] + p->z * ZBIAS) * dt; - p->life -= dt; - if(p->life < 0.0f) p->life = 0.0f; - - v->x = p->x; - v->y = p->y; - v->z = p->z; - v->w = 1.0f; - v->a = cround64(p->life * 255.0 / MAX_LIFE); - v->r = 0; - v->g = (v->a & 0xe0) >> 3; - v->b = (v->a & 0x1f) << 3; - ++v; - - ++p; - } -} - -void draw_particles(struct emitter *em) -{ - g3d_draw(G3D_POINTS, varr, PCOUNT); -} - - -int init_vfield_load(struct vfield *vf, const char *fname) -{ - FILE *fp; - int tmp; - - if(!(fp = fopen(fname, "rb"))) { - fprintf(stderr, "failed to open vector field: %s\n", fname); - return -1; - } - if(fread(&vf->width, sizeof vf->width, 1, fp) < 1 || - fread(&vf->height, sizeof vf->height, 1, fp) < 1) { - fprintf(stderr, "init_vfield_load: unexpected end of file while reading header\n"); - fclose(fp); - return -1; - } - - /* assume xsz is pow2 otherwise fuck you */ - tmp = vf->width - 1; - vf->xshift = 0; - while(tmp) { - ++vf->xshift; - tmp >>= 1; - } - - if(!(vf->v = malloc(vf->width * vf->height * sizeof *vf->v))) { - fprintf(stderr, "failed to allocate %dx%d vector field\n", vf->width, vf->height); - fclose(fp); - return -1; - } - if(fread(vf->v, sizeof *vf->v, vf->width * vf->height, fp) < vf->width * vf->height) { - fprintf(stderr, "init_vfield_load: unexpected end of file while reading %dx%d vector field\n", - vf->width, vf->height); - fclose(fp); - return -1; - } - fclose(fp); - - vf->pos.x = vf->pos.y = 0; - vf->size.x = vf->size.y = 1; - return 0; -} - -void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir) -{ - int px, py; - float tx, ty; - struct vec2 *p1, *p2; - struct vec2 left, right; - - x = ((x - vf->pos.x) / vf->size.x + 0.5f) * vf->width; - y = ((y - vf->pos.y) / vf->size.y + 0.5f) * vf->height; - x = floor(x); - y = floor(y); - - if(x < 0) x = 0; - if(y < 0) y = 0; - if(x > vf->width - 2) x = vf->width - 2; - if(y > vf->height - 2) y = vf->height - 2; - - px = (int)x; - py = (int)y; - - p1 = vf->v + (py << vf->xshift) + px; -#ifdef BILERP_FIELD - p2 = p1 + vf->width; - - tx = fmod(x, 1.0f); - ty = fmod(y, 1.0f); - - left.x = p1->x + (p2->x - p1->x) * ty; - left.y = p1->y + (p2->y - p1->y) * ty; - ++p1; - ++p2; - right.x = p1->x + (p2->x - p1->x) * ty; - right.y = p1->y + (p2->y - p1->y) * ty; - - dir->x = left.x + (right.x - left.x) * tx; - dir->y = left.y + (right.y - left.y) * ty; -#else - dir->x = p1->x; - dir->y = p1->y; -#endif - -#ifdef RANDOMIZE_FIELD - dir->x += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX; - dir->y += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX; -#endif -} diff --git a/src/noise.c b/src/noise.c new file mode 100644 index 0000000..b3b3f9c --- /dev/null +++ b/src/noise.c @@ -0,0 +1,505 @@ +#include +#include +#include "noise.h" + +/* ---- Ken Perlin's implementation of noise ---- */ +#define B 0x100 +#define BM 0xff +#define N 0x1000 +#define NP 12 /* 2^N */ +#define NM 0xfff + +#define s_curve(t) (t * t * (3.0f - 2.0f * t)) + +#define setup(elem, b0, b1, r0, r1) \ + do { \ + float t = elem + N; \ + b0 = ((int)t) & BM; \ + b1 = (b0 + 1) & BM; \ + r0 = t - (int)t; \ + r1 = r0 - 1.0f; \ + } while(0) + +#define setup_p(elem, b0, b1, r0, r1, p) \ + do { \ + float t = elem + N; \ + b0 = (((int)t) & BM) % p; \ + b1 = ((b0 + 1) & BM) % p; \ + r0 = t - (int)t; \ + r1 = r0 - 1.0f; \ + } while(0) + + +static int perm[B + B + 2]; /* permuted index from g_n onto themselves */ +static float grad3[B + B + 2][3]; /* 3D random gradients */ +static float grad2[B + B + 2][2]; /* 2D random gradients */ +static float grad1[B + B + 2]; /* 1D random ... slopes */ +static int tables_valid; + +#define init_once() if(!tables_valid) init_noise() + +static void init_noise() +{ + int i; + float len; + + /* calculate random gradients */ + for(i=0; i +#include +#include +#include +#include +#include "imago2.h" +#include "3dgfx.h" +#include "smoketxt.h" +#include "util.h" +#include "noise.h" + +/* if defined, use bilinear interpolation for dispersion field vectors */ +#define BILERP_FIELD +/* if defined randomize field vectors by RAND_FIELD_MAX */ +#define RANDOMIZE_FIELD + +#define RAND_FIELD_MAX 0.7 + +#define DFL_PCOUNT 4000 +#define DFL_MAX_LIFE 7.0f +#define DFL_PALPHA 1.0f +#define DFL_ZBIAS 0.25 +#define DFL_DRAG 0.95 +#define DFL_FORCE 0.07 +#define DFL_FREQ 0.085 + +struct vec2 { + float x, y; +}; + +struct vec3 { + float x, y, z; +}; + +struct particle { + float x, y, z; + float vx, vy, vz; /* velocity */ + int r, g, b; + float life; +}; + +struct emitter { + struct particle *plist; + int pcount; + + struct vec3 wind; + float drag; + float max_life; + + struct g3d_vertex *varr; +}; + +struct vfield { + struct vec2 pos, size; + + int width, height; + int xshift; + struct vec2 *v; +}; + + +struct smktxt { + struct emitter em; + struct vfield vfield; + + unsigned char *img_pixels; + int img_xsz, img_ysz; +}; + +static int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz); +static int load_vfield(struct vfield *vf, const char *fname); +static void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir); + +struct smktxt *create_smktxt(const char *imgname, const char *vfieldname) +{ + struct smktxt *stx; + + if(!imgname) return 0; + + if(!(stx = calloc(sizeof *stx, 1))) { + return 0; + } + if(!(stx->img_pixels = img_load_pixels(imgname, &stx->img_xsz, &stx->img_ysz, IMG_FMT_GREY8))) { + fprintf(stderr, "create_smktxt: failed to load particle spawnmap: %s\n", imgname); + free(stx); + return 0; + } + + stx->em.pcount = DFL_PCOUNT; + stx->em.wind.z = 0.01; + stx->em.drag = DFL_DRAG; + stx->em.max_life = DFL_MAX_LIFE; + + if(vfieldname) { + if(load_vfield(&stx->vfield, vfieldname) == -1) { + img_free_pixels(stx->img_pixels); + free(stx); + return 0; + } + } + + return stx; +} + +void destroy_smktxt(struct smktxt *stx) +{ + if(!stx) return; + + img_free_pixels(stx->img_pixels); + free(stx); +} + +int gen_smktxt_vfield(struct smktxt *stx, int xres, int yres, float xfreq, float yfreq) +{ + int i, j; + unsigned int tmp; + struct vec2 *vptr; + struct vfield *vf = &stx->vfield; + + if(!(vf->v = malloc(xres * yres * sizeof *vf->v))) { + fprintf(stderr, "failed to allocate %dx%d vector field\n", xres, yres); + return -1; + } + + vf->width = xres; + vf->height = yres; + vf->pos.x = vf->pos.y = 0.0f; + vf->size.x = vf->size.y = 1.0f; + + /* assume xres is pow2 otherwise fuck you */ + tmp = xres - 1; + vf->xshift = 0; + while(tmp) { + ++vf->xshift; + tmp >>= 1; + } + + vptr = vf->v; + for(i=0; ix = x / len; + vptr->y = y / len; + + ++vptr; + } + } + return 0; +} + +int dump_smktxt_vfield(struct smktxt *stx, const char *fname) +{ + FILE *fp; + int xsz, ysz; + + if(!(fp = fopen(fname, "wb"))) { + fprintf(stderr, "failed to open %s for writing: %s\n", fname, strerror(errno)); + return -1; + } + + xsz = stx->vfield.width; + ysz = stx->vfield.height; + + fwrite(&xsz, sizeof xsz, 1, fp); + fwrite(&ysz, sizeof ysz, 1, fp); + fwrite(stx->vfield.v, sizeof *stx->vfield.v, xsz * ysz, fp); + fclose(fp); + return 0; +} + +void set_smktxt_wind(struct smktxt *stx, float x, float y, float z) +{ + stx->em.wind.x = x; + stx->em.wind.y = y; + stx->em.wind.z = z; +} + +void set_smktxt_plife(struct smktxt *stx, float life) +{ + stx->em.max_life = life; +} + +void set_smktxt_pcount(struct smktxt *stx, int count) +{ + free(stx->em.plist); + stx->em.plist = 0; + stx->em.pcount = count; +} + +void set_smktxt_drag(struct smktxt *stx, float drag) +{ + stx->em.drag = drag; +} + +void update_smktxt(struct smktxt *stx, float dt) +{ + int i; + struct vec2 accel; + struct particle *p; + struct g3d_vertex *v; + + if(!stx->em.plist) { + if(init_emitter(&stx->em, stx->em.pcount, stx->img_pixels, stx->img_xsz, stx->img_ysz) == -1) { + fprintf(stderr, "failed to initialize emitter with %d particles\n", stx->em.pcount); + return; + } + } + + p = stx->em.plist; + v = stx->em.varr; + + for(i=0; iem.pcount; i++) { + vfield_eval(&stx->vfield, p->x, p->y, &accel); + p->x += p->vx * stx->em.drag * dt; + p->y += p->vy * stx->em.drag * dt; + p->z += p->vz * stx->em.drag * dt; + p->vx += (stx->em.wind.x + accel.x * DFL_FORCE) * dt; + p->vy += (stx->em.wind.y + accel.y * DFL_FORCE) * dt; + p->vz += (stx->em.wind.z + p->z * DFL_ZBIAS) * dt; + p->life -= dt; + if(p->life < 0.0f) p->life = 0.0f; + + v->x = p->x; + v->y = p->y; + v->z = p->z; + v->w = 1.0f; + v->a = cround64(p->life * 255.0f / stx->em.max_life); + v->r = 0; + v->g = (v->a & 0xe0) >> 3; + v->b = (v->a & 0x1f) << 3; + ++v; + + ++p; + } +} + +void draw_smktxt(struct smktxt *stx) +{ + g3d_draw(G3D_POINTS, stx->em.varr, stx->em.pcount); +} + + +static int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz) +{ + int i, x, y; + float aspect = (float)xsz / (float)ysz; + struct particle *p; + + free(em->varr); + if(!(em->varr = malloc(num * sizeof *em->varr))) { + fprintf(stderr, "failed to allocate particle vertex array (%d verts)\n", num); + return -1; + } + + free(em->plist); + if(!(em->plist = malloc(num * sizeof *em->plist))) { + free(em->varr); + return -1; + } + em->pcount = num; + + p = em->plist; + for(i=0; ix = (float)x / (float)xsz - 0.5; + p->y = -(float)y / (float)xsz + 0.5 / aspect; + p->z = ((float)i / (float)num * 2.0 - 1.0) * 0.005; + p->r = p->g = p->b = 255; + p->vx = p->vy = p->vz = 0.0f; + p->life = em->max_life; + ++p; + } + return 0; +} + +static int load_vfield(struct vfield *vf, const char *fname) +{ + FILE *fp; + int tmp; + + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "failed to open vector field: %s\n", fname); + return -1; + } + if(fread(&vf->width, sizeof vf->width, 1, fp) < 1 || + fread(&vf->height, sizeof vf->height, 1, fp) < 1) { + fprintf(stderr, "load_vfield: unexpected end of file while reading header\n"); + fclose(fp); + return -1; + } + + /* assume xsz is pow2 otherwise fuck you */ + tmp = vf->width - 1; + vf->xshift = 0; + while(tmp) { + ++vf->xshift; + tmp >>= 1; + } + + if(!(vf->v = malloc(vf->width * vf->height * sizeof *vf->v))) { + fprintf(stderr, "failed to allocate %dx%d vector field\n", vf->width, vf->height); + fclose(fp); + return -1; + } + if(fread(vf->v, sizeof *vf->v, vf->width * vf->height, fp) < vf->width * vf->height) { + fprintf(stderr, "load_vfield: unexpected end of file while reading %dx%d vector field\n", + vf->width, vf->height); + fclose(fp); + return -1; + } + fclose(fp); + + vf->pos.x = vf->pos.y = 0; + vf->size.x = vf->size.y = 1; + return 0; +} + +static void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir) +{ + int px, py; + float tx, ty; + struct vec2 *p1, *p2; + struct vec2 left, right; + + x = ((x - vf->pos.x) / vf->size.x + 0.5f) * vf->width; + y = ((y - vf->pos.y) / vf->size.y + 0.5f) * vf->height; + x = floor(x); + y = floor(y); + + if(x < 0) x = 0; + if(y < 0) y = 0; + if(x > vf->width - 2) x = vf->width - 2; + if(y > vf->height - 2) y = vf->height - 2; + + px = (int)x; + py = (int)y; + + p1 = vf->v + (py << vf->xshift) + px; +#ifdef BILERP_FIELD + p2 = p1 + vf->width; + + tx = fmod(x, 1.0f); + ty = fmod(y, 1.0f); + + left.x = p1->x + (p2->x - p1->x) * ty; + left.y = p1->y + (p2->y - p1->y) * ty; + ++p1; + ++p2; + right.x = p1->x + (p2->x - p1->x) * ty; + right.y = p1->y + (p2->y - p1->y) * ty; + + dir->x = left.x + (right.x - left.x) * tx; + dir->y = left.y + (right.y - left.y) * ty; +#else + dir->x = p1->x; + dir->y = p1->y; +#endif + +#ifdef RANDOMIZE_FIELD + dir->x += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX; + dir->y += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX; +#endif +} diff --git a/src/smoketxt.h b/src/smoketxt.h new file mode 100644 index 0000000..42ba49d --- /dev/null +++ b/src/smoketxt.h @@ -0,0 +1,21 @@ +#ifndef SMOKE_TEXT_H_ +#define SMOKE_TEXT_H_ + +struct smktxt; + +struct smktxt *create_smktxt(const char *imgname, const char *vfieldname); +void destroy_smktxt(struct smktxt *stx); + +int gen_smktxt_vfield(struct smktxt *stx, int xres, int yres, float xfreq, float yfreq); +int dump_smktxt_vfield(struct smktxt *stx, const char *fname); + +void set_smktxt_wind(struct smktxt *stx, float x, float y, float z); +void set_smktxt_plife(struct smktxt *stx, float life); +void set_smktxt_pcount(struct smktxt *stx, int count); +void set_smktxt_drag(struct smktxt *stx, float drag); + +void update_smktxt(struct smktxt *stx, float dt); +void draw_smktxt(struct smktxt *stx); + + +#endif /* SMOKE_TEXT_ */ -- 1.7.10.4