/* * Chips and sawdust: a fixed ring of little blocks, thrown, falling, lying. */ #include "world/chips.h" #include #include #include "render/shapes.h" #define CHIP_LIE 7.0f /* seconds a chip lies on the ground */ #define DUST_LIFE 1.3f /* seconds sawdust is in the air */ #define GRAVITY_C 9.81f static float rnd(Chips *c) { c->rng = c->rng * 1664525u + 1013904223u; return (float)((c->rng >> 8) & 0xFFFFu) / 65535.0f; } void Chips_init(Chips *c) { memset(c, 0, sizeof(*c)); c->rng = 0x51f15eedu; c->mesh = Shapes_chip(); c->puff = Shapes_puff(); } void Chips_destroy(Chips *c) { Mesh_destroy(&c->mesh); Mesh_destroy(&c->puff); memset(c, 0, sizeof(*c)); } void Chips_spray(Chips *c, Vec3 at, Vec3 dir, int n, bool dust) { float len = vec3_length(dir); dir = len > 1e-4f ? vec3_scale(dir, 1.0f / len) : vec3(0.0f, 1.0f, 0.0f); for (int k = 0; k < n; k++) { Chip *ch = &c->chips[c->next]; c->next = (c->next + 1) % CHIP_MAX; Vec3 jitter = vec3(rnd(c) - 0.5f, rnd(c) - 0.5f, rnd(c) - 0.5f); float speed = dust ? 1.8f + 1.6f * rnd(c) : 1.8f + 2.4f * rnd(c); Vec3 d = vec3_normalize(vec3_add(dir, vec3_scale(jitter, dust ? 0.9f : 1.3f))); *ch = (Chip){ .p = vec3_add(at, vec3_scale(jitter, 0.04f)), .v = vec3_add(vec3_scale(d, speed), vec3(0.0f, dust ? 0.3f : 1.2f + rnd(c), 0.0f)), .life = dust ? DUST_LIFE * (0.6f + 0.6f * rnd(c)) : CHIP_LIE + 3.0f * rnd(c), .size = dust ? 0.014f + 0.012f * rnd(c) : 0.04f + 0.04f * rnd(c), .angle = 6.28f * rnd(c), .spin = (rnd(c) - 0.5f) * 30.0f, .color = dust ? vec3(0.95f, 0.86f, 0.64f) : vec3_scale(vec3(0.93f, 0.78f, 0.52f), 0.85f + 0.2f * rnd(c)), .dust = dust, }; } } #define SMOKE_LIFE 1.8f void Chips_puff(Chips *c, Vec3 at, Vec3 dir, bool small) { Chip *ch = &c->chips[c->next]; c->next = (c->next + 1) % CHIP_MAX; Vec3 jitter = vec3(rnd(c) - 0.5f, rnd(c) - 0.5f, rnd(c) - 0.5f); *ch = (Chip){ .p = vec3_add(at, vec3_scale(jitter, 0.01f)), .v = vec3_add(vec3_scale(dir, small ? 0.05f : 0.6f + 0.3f * rnd(c)), vec3_add(vec3_scale(jitter, 0.1f), vec3(0.0f, 0.1f, 0.0f))), .life = (small ? 0.6f : 1.0f) * SMOKE_LIFE * (0.7f + 0.5f * rnd(c)), .size = small ? 0.006f : 0.012f, .angle = 6.28f * rnd(c), .spin = (rnd(c) - 0.5f) * 2.0f, .color = vec3_scale(vec3(0.86f, 0.87f, 0.9f), 0.9f + 0.1f * rnd(c)), .smoke = true, }; } void Chips_smoke(Chips *c, Vec3 at) { Chip *ch = &c->chips[c->next]; c->next = (c->next + 1) % CHIP_MAX; Vec3 jitter = vec3(rnd(c) - 0.5f, 0.0f, rnd(c) - 0.5f); float grey = 0.78f + 0.12f * rnd(c); *ch = (Chip){ .p = vec3_add(at, vec3_scale(jitter, 0.4f)), .v = vec3_add(vec3_scale(jitter, 0.2f), vec3(0.0f, 0.7f + 0.3f * rnd(c), 0.0f)), .life = 3.5f + 1.5f * rnd(c), .size = 0.25f + 0.12f * rnd(c), .angle = 6.28f * rnd(c), .spin = (rnd(c) - 0.5f) * 0.8f, .color = vec3(grey, grey, grey * 1.04f), .smoke = true, }; } void Chips_update(Chips *c, const Terrain *terrain, Vec3 wind, float dt) { for (int i = 0; i < CHIP_MAX; i++) { Chip *ch = &c->chips[i]; if (ch->life <= 0.0f) continue; ch->life -= dt; if (ch->smoke) { /* slows down to the wind's speed, rises, spreads */ float k = expf(-2.5f * dt), up = ch->v.y * k; ch->v = vec3_add(vec3_scale(ch->v, k), vec3_scale(wind, 0.8f * (1.0f - k))); ch->v.y = up + (ch->size > 0.05f ? 0.9f : 0.35f) * dt; /* (a fire's smoke: warm, rising more) */ ch->p = vec3_add(ch->p, vec3_scale(ch->v, dt)); ch->size += (ch->size > 0.05f ? 0.22f : 0.03f) * dt; /* (a fire's smoke spreads more) */ ch->angle += ch->spin * dt; continue; } if (ch->down) continue; float drag = ch->dust ? 3.5f : 0.6f, k = expf(-drag * dt), fall = ch->v.y * k; ch->v = vec3_add(vec3_scale(ch->v, k), ch->dust ? vec3_scale(wind, 0.7f * (1.0f - k)) : vec3_zero()); ch->v.y = fall; ch->v.y -= GRAVITY_C * (ch->dust ? 0.25f : 1.0f) * dt; ch->p = vec3_add(ch->p, vec3_scale(ch->v, dt)); ch->angle += ch->spin * dt; float ground = Terrain_height(terrain, ch->p.x, ch->p.z) + ch->size * 0.3f; if (ch->p.y < ground) { ch->p.y = ground; if (ch->dust) ch->life = fminf(ch->life, 0.3f); /* (sawdust settles and is gone) */ ch->down = true; ch->v = vec3_zero(); } } } #define SMOKE_ALPHA 0.5f /* how thick a puff of smoke is (see-through) */ void Chips_draw(const Chips *c, Renderer *renderer) { for (int i = 0; i < CHIP_MAX; i++) { const Chip *ch = &c->chips[i]; if (ch->life <= 0.0f) continue; float shrink = fminf(1.0f, ch->life / (ch->smoke ? 0.5f : ch->dust ? 0.3f : 1.5f)); /* (fading away at the end) */ float s = ch->size * (ch->smoke && ch->size > 0.05f ? 1.0f : shrink); /* (a fire's smoke: thinning out instead) */ Mat4 m = mat4_mul(mat4_translate(ch->p), mat4_mul(mat4_rotate_yxz(vec3(ch->down ? 0.0f : ch->angle, ch->angle * 0.7f, 0.0f)), mat4_scale(vec3(s, s * (ch->dust || ch->smoke ? 1.0f : 0.35f), s * (ch->dust || ch->smoke ? 1.0f : 0.7f))))); if (ch->smoke) Renderer_drawPuff(renderer, &c->puff, m, ch->color, SMOKE_ALPHA * fminf(1.0f, ch->life / 0.8f)); /* (thinning out at the end) */ else Renderer_drawMeshTinted(renderer, &c->mesh, m, ch->color, 0.0f); } }