raindrop-fx
Version:
Rain drop effect with WebGL
125 lines (107 loc) • 4.54 kB
text/typescript
import { div, mul, plus, vec2, Vector2 } from "@sardinefish/zogra-renderer";
import { random, randomRange } from "./random";
import { CollisionGrid, RaindropSimulator } from "./simulator";
import { lerp, Time } from "./utils";
export class RainDrop
{
pos: vec2;
density: number = 1;
velocity: vec2 = vec2.zero();
spread: vec2;
destroied = false;
parent?: RainDrop;
grid?: CollisionGrid;
gridIdx?: number;
private _mass: number = 0;
private _size: vec2 = vec2.zero();
private simulator: RaindropSimulator;
private resistance = 0;
private shifting = 0;
private lastTrailPos: vec2;
private nextTrailDistance: number;
private nextRandomTime = 0;
constructor(simulator: RaindropSimulator, pos: vec2, size: number, density = 1)
{
this.pos = pos;
this.simulator = simulator;
this.density = density;
this.lastTrailPos = pos.clone();
this.nextTrailDistance = randomRange(...simulator.options.trailDistance);
this.spread = vec2(simulator.options.initialSpread);
this.mass = (size * density) ** 2;
}
get mass() { return this._mass; }
set mass(m: number)
{
this._mass = m;
const sqrtM = Math.sqrt(m) / this.density;
this._size.x = (this.spread.x + 1) * sqrtM;
this._size.y = (this.spread.y + 1) * sqrtM;
// this._size = mul(plus(this.spread, vec2.one()), Math.sqrt(m));
}
get size(): vec2
{
return this._size;
}
get mergeDistance()
{
return this.size.x * (1 + this.spread.x) * 0.16 * this.simulator.options.colliderSize;
}
get options() { return this.simulator.options }
updateRaindrop(time: Time)
{
if (this.nextRandomTime <= time.total)
{
this.nextRandomTime = time.total + randomRange(...this.simulator.options.motionInterval)
this.randomMotion();
}
this.mass -= this.simulator.options.evaporate * time.dt;
const force = this.options.gravity * this.mass - this.resistance;
const acceleration = force / this.mass;
this.velocity.y -= acceleration * time.dt;
if (this.velocity.y > 0)
this.velocity.y = 0;
this.velocity.x = Math.abs(this.velocity.y) * this.shifting;
this.pos.x += this.velocity.x * time.dt;
this.pos.y += this.velocity.y * time.dt;
// this.pos.plus(mul(this.velocity, vec2(time.dt)));
const spreadByVelocity = this.simulator.options.velocitySpread * 2 * Math.atan(Math.abs(this.velocity.y * 0.005)) / Math.PI;
this.spread.y = Math.max(this.spread.y, spreadByVelocity);
this.spread.x *= Math.pow(this.simulator.options.shrinkRate, time.dt);
this.spread.y *= Math.pow(this.simulator.options.shrinkRate, time.dt);
// this.spread.y += Math.abs(this.velocity.y) * 0.0001;
if (Vector2.distanceSquared(this.lastTrailPos, this.pos) > this.nextTrailDistance * this.nextTrailDistance)
{
this.split();
}
}
split()
{
// return;
if (this.mass < 1000)
return;
let size = this.size.x * randomRange(...this.simulator.options.trailDropSize);
const pos = plus(vec2(randomRange(-5, 5), this.size.y / 4), this.pos);
let trailDrop = this.simulator.spawner.spawn(pos.clone(), size, this.simulator.options.trailDropDensity);
trailDrop.spread = vec2(0.1, Math.abs(this.velocity.y) * 0.01 * this.options.trailSpread);
trailDrop.parent = this;
this.mass -= trailDrop.mass;
this.simulator.add(trailDrop);
this.lastTrailPos = this.pos.clone();
this.nextTrailDistance = randomRange(...this.simulator.options.trailDistance);
}
randomMotion()
{
const maxResistance = lerp(...this.simulator.options.spawnSize, 1 - this.simulator.options.slipRate) ** 2 * 4;
this.resistance = randomRange(0, 1) * this.options.gravity * maxResistance;
this.shifting = random() * randomRange(...this.simulator.options.xShifting);
}
merge(target: RainDrop)
{
const selfMomentum = mul(this.velocity, this.mass);
const targetMomentum = mul(target.velocity, target.mass);
const momentum = plus(selfMomentum, targetMomentum);
this.mass += target.mass;
this.velocity = div(momentum, this.mass);
}
}