void solveCollision(float dt) { // This function detects particles which are crossing boundary of bodies // and modifies velocities of them so that they will move just in front of // boundary. This function function also applies the reaction force to // bodies as precisely as the numerical stability is kept. FluidAABB aabb = new FluidAABB(); aabb.lowerBound.x = +float.MaxValue; aabb.lowerBound.y = +float.MaxValue; aabb.upperBound.x = -float.MaxValue; aabb.upperBound.y = -float.MaxValue; for (int i = 0; i < count; i++) { FluidParticle fp = particleBuffer [i]; Vector2 p1 = fp.position; Vector2 p2 = p1 + dt * fp.velocity; aabb.lowerBound = Vector2.Min(aabb.lowerBound, Vector2.Min(p1, p2)); aabb.upperBound = Vector2.Max(aabb.upperBound, Vector2.Max(p1, p2)); } SolveCollisionCallback callback = new SolveCollisionCallback(this, dt); queryAABB(callback, aabb); }
void solveDamping(float dt) { // reduces normal velocity of each contact float linearDamping = fluidSystemDef.dampingStrength; float quadraticDamping = 1 / getCriticalVelocity(dt); for (int i = 0; i < bodyContactBuffer.Count; i++) { FluidParticleBodyContact contact = bodyContactBuffer [i]; FluidParticle a = contact.fp; Vector2 p = a.position; Vector2 v = contact.body.velocity - a.velocity; float vn = Vector2.Dot(v, contact.normal); if (vn < 0) { float damping = Mathf.Max(linearDamping * contact.weight, Mathf.Min(-quadraticDamping * vn, 0.5f)); Vector2 f = damping * contact.mass * vn * contact.normal; a.velocity += getParticleInvMass() * f; contact.body.AddForceAtPosition(-f / dt, p); } } for (int i = 0; i < contactBuffer.Count; i++) { FluidParticleContact contact = contactBuffer [i]; Vector2 v = contact.b.velocity - contact.a.velocity; float vn = Vector2.Dot(v, contact.normal); if (vn < 0) { float damping = Mathf.Max(linearDamping * contact.weight, Mathf.Min(-quadraticDamping * vn, 0.5f)); Vector2 f = damping * vn * contact.normal; contact.a.velocity += f; contact.b.velocity -= f; } } }
public override void computeDistance(FluidParticle fp, out float distance, out Vector2 normal) { Vector2 center = new Vector2(transform.position.x, transform.position.y) + circle.offset; Vector2 d = fp.position - center; float d1 = d.magnitude; distance = d1 - radius; normal = d / d1; }
public void destroyParticle(FluidParticle fp, bool callDestructionListener) { FluidParticleType flags = FluidParticleType.ZombieParticle; if (callDestructionListener) { flags |= FluidParticleType.DestructionListenerParticle; } setParticleFlags(fp, fp.flags | flags); }
void solveWall() { for (int i = 0; i < count; i++) { FluidParticle fp = particleBuffer [i]; if ((fp.flags & FluidParticleType.WallParticle) > 0) { fp.velocity = Vector2.zero; } } }
void solveForce(float dt) { float velocityPerForce = dt * getParticleInvMass(); for (int i = 0; i < count; i++) { FluidParticle fp = particleBuffer [i]; fp.velocity += velocityPerForce * fp.force; } hasForce = false; }
void setParticleFlags(FluidParticle fp, FluidParticleType newFlags) { if ((fp.flags & ~newFlags) > 0) { needsUpdateAllParticleFlags = true; } if ((~allParticleFlags & newFlags) > 0) { allParticleFlags |= newFlags; } fp.flags = newFlags; }
public override void reportFixtureAndParticle(FluidFixtureShape fixture, FluidParticle fp) { //Rigidbody2D body = fixture.GetComponent<Rigidbody2D> (); Vector2 ap = fp.position; Vector2 av = fp.velocity; Ray2D ray = new Ray2D(); //RaycastHit2D hit; if (fluidSystem.iterationIndex == 0) { // // Put 'ap' in the local space of the previous frame // b2Vec2 p1 = b2MulT(body->m_xf0, ap); // if (fixture->GetShape()->GetType() == b2Shape::e_circle) // { // // Make relative to the center of the circle // p1 -= body->GetLocalCenter(); // // Re-apply rotation about the center of the // // circle // p1 = b2Mul(body->m_xf0.q, p1); // // Subtract rotation of the current frame // p1 = b2MulT(body->m_xf.q, p1); // // Return to local space // p1 += body->GetLocalCenter(); // } // // Return to global space and apply rotation of current frame // input.p1 = b2Mul(body->m_xf, p1); ray.origin = fp.prevPosition; } else { ray.origin = ap; } ray.direction = av; //hit = Physics2D.Raycast (ray.origin, ray.direction, av.magnitude * dt); Physics2D.Raycast(ray.origin, ray.direction, av.magnitude * dt); //if (hit) { // b2Vec2 p = // (1 - output.fraction) * input.p1 + // output.fraction * input.p2 + // b2_linearSlop * n; //Vector2 v = (hit.point - ap) / dt; // fp.velocity = v; //Vector2 f = fluidSystem.getParticleMass () * (av - v); // fluidSystem.particleApplyForce(fp, f); //} }
public override void reportFixtureAndParticle(FluidFixtureShape fixture, FluidParticle fp) { float d; Vector2 n; fixture.computeDistance(fp, out d, out n); if ((d < fluidSystem.particleDiameter) && ((fp.flags & FluidParticleType.WallParticle) == 0)) { Rigidbody2D b = fixture.GetComponent <Rigidbody2D> (); //Vector2 bp = b.transform.position; float bm = b.mass; // b2Vec2 bp = b->GetWorldCenter(); // float32 bI = // b->GetInertia() - bm * b->GetLocalCenter().LengthSquared(); float invBm = bm > 0 ? 1 / bm : 0; // float32 invBI = bI > 0 ? 1 / bI : 0; float invAm = (fp.flags & FluidParticleType.WallParticle) > 0 ? 0 : fluidSystem.getParticleInvMass(); //Vector2 rp = fp.position - bp; // float rpn = rp * n; // float32 rpn = b2Cross(rp, n); // float32 invM = invAm + invBm + invBI * rpn * rpn; float invM = invAm + invBm; FluidParticleBodyContact contact = new FluidParticleBodyContact { fp = fp, body = b, fixture = fixture, weight = 1 - d * fluidSystem.inverseDiameter, normal = -n, mass = invM > 0 ? 1 / invM : 0 }; if (fixture.isTrigger()) { fluidSystem.triggerContactBuffer.Add(contact); fixture.notifyOnTrigger(contact); } else { fluidSystem.bodyContactBuffer.Add(contact); } // m_system->DetectStuckParticle(a); } }
// void OnGUI () { // GUI.Label (new Rect (10, 50, 100, 100), count + ""); // //// for (int i=0;i<count;i++) { //// Handles.Label(proxyBuffer[i].fp.position, i+""); //// } // } #if UNITY_EDITOR && FLUID_DEBUG void OnDrawGizmos() { if (proxyBuffer != null && count > 0) { int i = 0; foreach (FluidProxy proxy in proxyBuffer) { FluidParticle fp = proxy.fp; Handles.color = fp.color; //Handles.DrawSolidDisc(new Vector3 (fp.position.x, fp.position.y, 0), Vector3.back, 0.05f); //Handles.DotCap (0, fp.position, Quaternion.identity, 0.05f); //Handles.DrawLine(fp.position,fp.position + fp.velocity * 0.1f); Handles.Label(fp.position, (int)fp.temperature + ""); } } }
void addContact(FluidParticle a, FluidParticle b) { Vector2 d = b.position - a.position; float distBtParticlesSq = d.sqrMagnitude; if (distBtParticlesSq < squaredDiameter) { float dist = Mathf.Sqrt(distBtParticlesSq); FluidParticleContact contact = new FluidParticleContact(); contactBuffer.Add(contact); contact.a = a; contact.b = b; contact.flags = a.flags | b.flags; contact.weight = 1 - dist * inverseDiameter; // * (b.mass / a.mass); contact.normal = d / dist; } }
public FluidParticle createParticle(FluidParticleDef def) { if (count >= fluidSystemDef.maxCount) { return(null); } count++; FluidParticle fp = new FluidParticle(); particleBuffer.Add(fp); setParticleFlags(fp, def.flags); fp.position = def.position; fp.prevPosition = def.position; fp.velocity = def.velocity; fp.weight = 0; fp.force = Vector2.zero; fp.mass = def.mass; fp.staticPressure = 0; fp.depth = 0; fp.color = def.color; fp.foamColor = def.foamColor; fp.foamWeightThreshold = def.foamWeightThreshold; fp.temperature = def.temperature; fp.freezingTemperature = def.freezingTemperature; fp.boilingTemperature = def.boilingTemperature; FluidProxy proxy = new FluidProxy(); proxyBuffer.Add(proxy); proxy.fp = fp; // bool finiteLifetime = def.lifetime > 0; // if (m_expirationTimeBuffer.data || finiteLifetime) // { // SetParticleLifetime(index, finiteLifetime ? def.lifetime : // ExpirationTimeToLifetime( // -GetQuantizedTimeElapsed())); // // Add a reference to the newly added particle to the end of the // // queue. // m_indexByExpirationTimeBuffer.data[index] = index; // } return(fp); }
public override void computeDistance(FluidParticle fp, out float distance, out Vector2 normal) { bool onEdge = computeDistance(vertices, fp.position, out distance, out normal); if (onEdge) { float dot = Vector2.Dot(normals [0], normal); if (Mathf.Approximately(dot, -1f)) { if (distance < scaledWidth) { distance = -distance; normal = normals [0]; } else { distance -= scaledWidth; } } } }
public override void computeDistance(FluidParticle fp, out float distance, out Vector2 normal) { Vector2 p = fp.position; float maxDistance = -float.MaxValue; Vector2 normalForMaxDistance = p; for (int i = 0; i < vertices.Length; i++) { float dot = Vector2.Dot(normals [i], p - vertices [i]); if (dot > maxDistance) { maxDistance = dot; normalForMaxDistance = normals [i]; } } if (maxDistance > 0) { Vector2 minDistance = normalForMaxDistance; float minDistance2 = maxDistance * maxDistance; for (int i = 0; i < vertices.Length; i++) { Vector2 dist = p - vertices [i]; float dist2 = dist.sqrMagnitude; if (minDistance2 > dist2) { minDistance = dist; minDistance2 = dist2; } } distance = Mathf.Sqrt(minDistance2); normal = minDistance.normalized; } else { distance = maxDistance; normal = normalForMaxDistance; } }
void solveStaticPressure(float dt) { float criticalPressure = getCriticalPressure(dt); float pressurePerWeight = fluidSystemDef.staticPressureStrength * criticalPressure; float maxPressure = maxParticlePressure * criticalPressure; float relaxation = fluidSystemDef.staticPressureRelaxation; for (int t = 0; t < fluidSystemDef.staticPressureIterations; t++) { for (int i = 0; i < count; i++) { particleBuffer [i].accumulation = 0; } for (int i = 0; i < contactBuffer.Count; i++) { FluidParticleContact contact = contactBuffer [i]; if ((contact.flags & FluidParticleType.StaticPressureParticle) > 0) { contact.a.accumulation += contact.weight * contact.b.staticPressure; contact.b.accumulation += contact.weight * contact.a.staticPressure; } } for (int i = 0; i < count; i++) { FluidParticle fp = particleBuffer [i]; if ((fp.flags & FluidParticleType.StaticPressureParticle) > 0) { float h = (fp.accumulation + pressurePerWeight * (fp.weight - minParticleWeight)) / (fp.weight + relaxation); fp.staticPressure = Mathf.Clamp(h, 0.0f, maxPressure); } else { fp.staticPressure = 0; } } } }
void solvePressure(float dt) { // calculates pressure as a linear function of density float criticalPressure = getCriticalPressure(dt); float pressurePerWeight = fluidSystemDef.pressureStrength * criticalPressure; float maxPressure = maxParticlePressure * criticalPressure; for (int i = 0; i < count; i++) { FluidParticle fp = particleBuffer [i]; float h = pressurePerWeight * Mathf.Max(0f, fp.weight - minParticleWeight); fp.accumulation = Mathf.Min(h, maxPressure); } // // ignores particles which have their own repulsive force // if (m_allParticleFlags & k_noPressureFlags) // { // for (int32 i = 0; i < m_count; i++) // { // if (m_flagsBuffer.data[i] & k_noPressureFlags) // { // m_accumulationBuffer[i] = 0; // } // } // } // static pressure if ((allParticleFlags & FluidParticleType.StaticPressureParticle) > 0) { for (int i = 0; i < count; i++) { FluidParticle fp = particleBuffer [i]; if ((fp.flags & FluidParticleType.StaticPressureParticle) > 0) { fp.accumulation += fp.staticPressure; } } } // applies pressure between each particles in contact float velocityPerPressure = dt / (fluidSystemDef.density * particleDiameter); for (int i = 0; i < bodyContactBuffer.Count; i++) { FluidParticleBodyContact contact = bodyContactBuffer [i]; FluidParticle a = contact.fp; Rigidbody2D b = contact.body; Vector2 p = a.position; float h = a.accumulation + pressurePerWeight * contact.weight; Vector2 f = velocityPerPressure * contact.weight * contact.mass * h * contact.normal; a.velocity -= getParticleInvMass() * f; b.AddForceAtPosition(f / dt, p); } float strength = fluidSystemDef.temperaturMixingStrength; for (int i = 0; i < contactBuffer.Count; i++) { FluidParticleContact contact = contactBuffer [i]; FluidParticle a = contact.a; FluidParticle b = contact.b; float h = a.accumulation + b.accumulation; Vector2 f = velocityPerPressure * contact.weight * h * contact.normal; a.velocity -= f; b.velocity += f; float t = (b.temperature - a.temperature) * strength; a.temperature += t; b.temperature -= t; if (Mathf.Abs(a.mass - b.mass) >= 0.01f) { if (b.mass < a.mass) { a = contact.b; b = contact.a; } a.velocity.y += fluidSystemDef.densityStrength; b.velocity.y -= fluidSystemDef.densityStrength; } else if (Mathf.Abs(a.temperature - b.temperature) >= 0.1f) { if (b.temperature > a.temperature) { a = contact.b; b = contact.a; } a.velocity.y += fluidSystemDef.temperatureStrength; b.velocity.y -= fluidSystemDef.temperatureStrength; } } }
public abstract void reportFixtureAndParticle(FluidFixtureShape fixture, FluidParticle fp);
void solve(float dt) { if (count == 0) { return; } // if (m_expirationTimeBuffer.data) // { // SolveLifetimes(step); // } if ((allParticleFlags & FluidParticleType.ZombieParticle) > 0) { solveZombie(); } if (needsUpdateAllParticleFlags) { updateAllParticleFlags(); } // if (m_needsUpdateAllGroupFlags) // { // UpdateAllGroupFlags(); // } if (paused) { return; } float subStep = dt / particleIterations; for (iterationIndex = 0; iterationIndex < particleIterations; iterationIndex++) { ++timestamp; updateContacts(false); updateBodyContacts(); computeWeight(); // if (m_allGroupFlags & b2_particleGroupNeedsUpdateDepth) // { // ComputeDepth(); // } // if (m_allParticleFlags & b2_reactiveParticle) // { // UpdatePairsAndTriadsWithReactiveParticles(); // } if (hasForce) { solveForce(subStep); } if ((allParticleFlags & FluidParticleType.ViscousParticle) > 0) { solveViscous(); } // if (m_allParticleFlags & b2_repulsiveParticle) // { // SolveRepulsive(subStep); // } if ((allParticleFlags & FluidParticleType.PowderParticle) > 0) { solvePowder(subStep); } if ((allParticleFlags & FluidParticleType.TensileParticle) > 0) { solveTensile(subStep); } // if (m_allGroupFlags & b2_solidParticleGroup) // { // SolveSolid(subStep); // } if ((allParticleFlags & FluidParticleType.ColorMixingParticle) > 0) { solveColorMixing(); } solveGravity(subStep); if ((allParticleFlags & FluidParticleType.StaticPressureParticle) > 0) { solveStaticPressure(subStep); } solvePressure(subStep); solveDamping(subStep); // if (m_allParticleFlags & k_extraDampingFlags) // { // SolveExtraDamping(); // } // // SolveElastic and SolveSpring refer the current velocities for // // numerical stability, they should be called as late as possible. // if (m_allParticleFlags & b2_elasticParticle) // { // SolveElastic(subStep); // } // if (m_allParticleFlags & b2_springParticle) // { // SolveSpring(subStep); // } limitVelocity(subStep); // if (m_allGroupFlags & b2_rigidParticleGroup) // { // SolveRigidDamping(); // } // if (m_allParticleFlags & b2_barrierParticle) // { // SolveBarrier(subStep); // } // // SolveCollision, SolveRigid and SolveWall should be called after // // other force functions because they may require particles to have // // specific velocities. // solveCollision(subStep); // if (m_allGroupFlags & b2_rigidParticleGroup) // { // SolveRigid(subStep); // } if ((allParticleFlags & FluidParticleType.WallParticle) > 0) { solveWall(); } // if (iterationIndex == 0) { // for (int i=0; i<count; i++) { // FluidParticle fp = particleBuffer [i]; // fp.prevPosition = fp.position; // } // } for (int i = 0; i < count; i++) { FluidParticle fp = particleBuffer [i]; fp.prevPosition = fp.position; fp.position += subStep * fp.velocity; } } }
public abstract void computeDistance(FluidParticle fp, out float distance, out Vector2 normal);
public void particleApplyForce(FluidParticle fp, Vector2 force) { prepareForceBuffer(); fp.force += force; }