12 /* if defined, use bilinear interpolation for dispersion field vectors */
14 /* if defined randomize field vectors by RAND_FIELD_MAX */
15 #define RANDOMIZE_FIELD
17 #define RAND_FIELD_MAX 0.7
19 #define DFL_PCOUNT 4000
20 #define DFL_MAX_LIFE 7.0f
21 #define DFL_PALPHA 1.0f
22 #define DFL_ZBIAS 0.25
24 #define DFL_FORCE 0.07
25 #define DFL_FREQ 0.085
37 float vx, vy, vz; /* velocity */
43 struct particle *plist;
50 struct g3d_vertex *varr;
54 struct vec2 pos, size;
66 unsigned char *img_pixels;
70 static int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz);
71 static int load_vfield(struct vfield *vf, const char *fname);
72 static void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir);
74 struct smktxt *create_smktxt(const char *imgname, const char *vfieldname)
78 if(!imgname) return 0;
80 if(!(stx = calloc(sizeof *stx, 1))) {
83 if(!(stx->img_pixels = img_load_pixels(imgname, &stx->img_xsz, &stx->img_ysz, IMG_FMT_GREY8))) {
84 fprintf(stderr, "create_smktxt: failed to load particle spawnmap: %s\n", imgname);
89 stx->em.pcount = DFL_PCOUNT;
90 stx->em.wind.z = 0.01;
91 stx->em.drag = DFL_DRAG;
92 stx->em.max_life = DFL_MAX_LIFE;
95 if(load_vfield(&stx->vfield, vfieldname) == -1) {
96 img_free_pixels(stx->img_pixels);
105 void destroy_smktxt(struct smktxt *stx)
109 img_free_pixels(stx->img_pixels);
113 int gen_smktxt_vfield(struct smktxt *stx, int xres, int yres, float xfreq, float yfreq)
118 struct vfield *vf = &stx->vfield;
120 if(!(vf->v = malloc(xres * yres * sizeof *vf->v))) {
121 fprintf(stderr, "failed to allocate %dx%d vector field\n", xres, yres);
127 vf->pos.x = vf->pos.y = 0.0f;
128 vf->size.x = vf->size.y = 1.0f;
130 /* assume xres is pow2 otherwise fuck you */
139 for(i=0; i<yres; i++) {
140 for(j=0; j<xres; j++) {
141 float x = noise3(j * xfreq, i * yfreq, 0.5);
142 float y = noise3(j * xfreq, i * yfreq, 10.5);
143 float len = sqrt(x * x + y * y);
144 if(len == 0.0f) len = 1.0f;
155 int dump_smktxt_vfield(struct smktxt *stx, const char *fname)
160 if(!(fp = fopen(fname, "wb"))) {
161 fprintf(stderr, "failed to open %s for writing: %s\n", fname, strerror(errno));
165 xsz = stx->vfield.width;
166 ysz = stx->vfield.height;
168 fwrite(&xsz, sizeof xsz, 1, fp);
169 fwrite(&ysz, sizeof ysz, 1, fp);
170 fwrite(stx->vfield.v, sizeof *stx->vfield.v, xsz * ysz, fp);
175 void set_smktxt_wind(struct smktxt *stx, float x, float y, float z)
182 void set_smktxt_plife(struct smktxt *stx, float life)
184 stx->em.max_life = life;
187 void set_smktxt_pcount(struct smktxt *stx, int count)
191 stx->em.pcount = count;
194 void set_smktxt_drag(struct smktxt *stx, float drag)
199 void update_smktxt(struct smktxt *stx, float dt)
204 struct g3d_vertex *v;
207 if(init_emitter(&stx->em, stx->em.pcount, stx->img_pixels, stx->img_xsz, stx->img_ysz) == -1) {
208 fprintf(stderr, "failed to initialize emitter with %d particles\n", stx->em.pcount);
216 for(i=0; i<stx->em.pcount; i++) {
217 vfield_eval(&stx->vfield, p->x, p->y, &accel);
218 p->x += p->vx * stx->em.drag * dt;
219 p->y += p->vy * stx->em.drag * dt;
220 p->z += p->vz * stx->em.drag * dt;
221 p->vx += (stx->em.wind.x + accel.x * DFL_FORCE) * dt;
222 p->vy += (stx->em.wind.y + accel.y * DFL_FORCE) * dt;
223 p->vz += (stx->em.wind.z + p->z * DFL_ZBIAS) * dt;
225 if(p->life < 0.0f) p->life = 0.0f;
231 v->a = cround64(p->life * 255.0f / stx->em.max_life);
233 v->g = (v->a & 0xe0) >> 3;
234 v->b = (v->a & 0x1f) << 3;
241 void draw_smktxt(struct smktxt *stx)
243 g3d_draw(G3D_POINTS, stx->em.varr, stx->em.pcount);
247 static int init_emitter(struct emitter *em, int num, unsigned char *map, int xsz, int ysz)
250 float aspect = (float)xsz / (float)ysz;
254 if(!(em->varr = malloc(num * sizeof *em->varr))) {
255 fprintf(stderr, "failed to allocate particle vertex array (%d verts)\n", num);
260 if(!(em->plist = malloc(num * sizeof *em->plist))) {
267 for(i=0; i<num; i++) {
271 } while(map[y * xsz + x] < 128);
273 p->x = (float)x / (float)xsz - 0.5;
274 p->y = -(float)y / (float)xsz + 0.5 / aspect;
275 p->z = ((float)i / (float)num * 2.0 - 1.0) * 0.005;
276 p->r = p->g = p->b = 255;
277 p->vx = p->vy = p->vz = 0.0f;
278 p->life = em->max_life;
284 static int load_vfield(struct vfield *vf, const char *fname)
289 if(!(fp = fopen(fname, "rb"))) {
290 fprintf(stderr, "failed to open vector field: %s\n", fname);
293 if(fread(&vf->width, sizeof vf->width, 1, fp) < 1 ||
294 fread(&vf->height, sizeof vf->height, 1, fp) < 1) {
295 fprintf(stderr, "load_vfield: unexpected end of file while reading header\n");
300 /* assume xsz is pow2 otherwise fuck you */
308 if(!(vf->v = malloc(vf->width * vf->height * sizeof *vf->v))) {
309 fprintf(stderr, "failed to allocate %dx%d vector field\n", vf->width, vf->height);
313 if(fread(vf->v, sizeof *vf->v, vf->width * vf->height, fp) < vf->width * vf->height) {
314 fprintf(stderr, "load_vfield: unexpected end of file while reading %dx%d vector field\n",
315 vf->width, vf->height);
321 vf->pos.x = vf->pos.y = 0;
322 vf->size.x = vf->size.y = 1;
326 static void vfield_eval(struct vfield *vf, float x, float y, struct vec2 *dir)
330 struct vec2 *p1, *p2;
331 struct vec2 left, right;
333 x = ((x - vf->pos.x) / vf->size.x + 0.5f) * vf->width;
334 y = ((y - vf->pos.y) / vf->size.y + 0.5f) * vf->height;
340 if(x > vf->width - 2) x = vf->width - 2;
341 if(y > vf->height - 2) y = vf->height - 2;
346 p1 = vf->v + (py << vf->xshift) + px;
353 left.x = p1->x + (p2->x - p1->x) * ty;
354 left.y = p1->y + (p2->y - p1->y) * ty;
357 right.x = p1->x + (p2->x - p1->x) * ty;
358 right.y = p1->y + (p2->y - p1->y) * ty;
360 dir->x = left.x + (right.x - left.x) * tx;
361 dir->y = left.y + (right.y - left.y) * ty;
367 #ifdef RANDOMIZE_FIELD
368 dir->x += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX;
369 dir->y += ((float)rand() / RAND_MAX - 0.5) * RAND_FIELD_MAX;