public IEnumerable GetNeighbourIndex(FluidParticle particle) { for (int xOff = -1; xOff < 2; xOff++) { for (int yOff = -1; yOff < 2; yOff++) { // Own index // Neighbour index int x = GetGridIndexX(ref particle) + xOff; int y = GetGridIndexY(ref particle) + yOff; // Clamp if (x > -1 && x < this.Width && y > -1 && y < this.Height) { if (m_grid[x] != null) { if (m_grid[x][y] != null) { ArrayList idxList = (ArrayList)m_grid[x][y].Clone(); if (idxList != null) { // Return neighbours index foreach (int idx in m_grid[x][y]) { yield return(idx); } idxList.Clear(); } } } } } } }
Vector3 calcPressure(FluidParticle p) { //calcWeight = p.CalcMatrixValue; //calcResult = p.calcRightSideValue; //A = SparseMatrix.Create(p.nearbyParticles.Length-1, p.nearbyParticles.Length-1, calcWeight); //B = Vector<double>.Build.Dense(p.nearbyParticles.Length-1, calcResult); //X = Vector<double>.Build.Dense(p.nearbyParticles.Length); //X = Solve(A, B, X); //X = A.Solve(B); /* calcWeight = p.CalcMatrixValue2; * calcResult = p.calcRightSideValue2; * A = SparseMatrix.Create(p.nearbyParticles.Length-1, p.nearbyParticles.Length, calcWeight); * B = Vector<double>.Build.Dense(p.nearbyParticles.Length-1, calcResult); */// = Vector<double>.Build.Dense(p.nearbyParticles.Length); //X = A.SolveIterative(B, new BiCgStab()); Vector3 result = new Vector3(); for (int i = 1; i < p.nearbyParticles.Length; i++) { temp = p.nearbyParticles[i].Pos() - p.Pos(); result = resultP + ((float)(((X.At(p.nearbyParticles[i].ID) - X.At(p.ID)) / Mathf.Pow(temp.magnitude, 2) * p.weights[i])) * temp); } result = result * (float)(3d / p.defaultParticleDensity); return(result); }
/*Go through all the Blobs, and travel from the center outwards in a negative Z direction * until we reach the surface, then begin to recurse around the surface. This isn't flawless * if the blob isn't completely within the lattice boundaries in the minimal Z axis and no * other blob that does check out is in contact with it. The blob will dissapear, but otherwise * works well*/ private void march() { int i, jx, jy, jz; for (i = 0; i < this.GetComponent <FluidBehaviour>().Particles.Count; i++) { FluidParticle pb = this.GetComponent <FluidBehaviour>().Particles[i]; jx = (int)((pb.Position.x + .5f) * dimX); jy = (int)((pb.Position.y + .5f) * dimY); jz = (int)((pb.Position.z + .5f) * dimZ); while (jz >= 0) { mcCube cube = getCube(jx, jy, jz); if (cube != null && cube.cntr < pctr) { cube.cntr = pctr; if (doCube(cube)) { recurseCube(cube); jz = -1; } } else { jz = -1; } jz -= 1; } } }
// Start is called before the first frame update public void Initialze() { size = (int)((resolution.x) * (resolution.y) * (resolution.z)); particles = new FluidParticle[size]; int x = 0, y = 0, z = 0; for (int i = 0; i < size; i++) { particles[i] = new FluidParticle(); particles[i].position = new Vector3(x, y, z); x++; if (x % resolution.x == 0) { x = 0; y++; if (y % resolution.y == 0) { y = 0; z++; if (z % resolution.z == 0) { z = 0; } } } } }
/// <summary> /// Sets initial particles /// </summary> /// <returns></returns> public static List <FluidParticle> InitSPH() { var particles = new List <FluidParticle>(); var xStart = 5; var yStart = 9; var columns = 5; var rows = 5; for (int i = 0; i < columns; i++) { for (int j = 0; j < rows; j++) { FluidParticle particle; // Adds a bit of jitter to allow particles to flow out if (j % 2 == 0) { particle = new FluidParticle(xStart + i + 0.3f, yStart - j); particles.Add(particle); } else { particle = new FluidParticle(xStart + i, yStart - j); particles.Add(particle); } } } return(particles); }
void Update() { if (m_Rigidbodies == null) { return; } if (!fluid.isPlaying && !m_Paused) { m_Paused = true; int l = Mathf.Min(m_Rigidbodies.Length, fluid.particleCount); for (int i = 0; i < l; ++i) { // Get objects Rigidbody2D body = m_Rigidbodies[i]; FluidParticle particle = fluid.GetParticle(i); body.transform.position = particle.position; body.velocity = Vector3.zero; body.isKinematic = true; body.Sleep(); } } else if (fluid.isPlaying && m_Paused) { m_Paused = false; } }
public static ArrayList Create(int nParticles, float cellSpace, Rect domain, float particleMass) { ArrayList particles = new ArrayList(nParticles); float x0 = domain.x + cellSpace; float x = x0; float y = domain.y; float z = domain.width; float z0 = domain.width + cellSpace; float z0m = -domain.width + cellSpace; for (int i = 0; i < nParticles; i++) { if (x == x0) { y += cellSpace; } if (z == z0 || z == z0m) { x += cellSpace; } Vector3 pos = new Vector3(x, y, z); FluidParticle p = new FluidParticle(); p.Position = pos; p.PositionOld = pos; p.Mass = particleMass; particles.Add(p); x = x + cellSpace < domain.width ? x + cellSpace : x0; } return(particles); }
public void GetNeighbourIndex(ref FluidParticle particle, out ArrayList neighbours) { neighbours = null; for (int xOff = -1; xOff < 2; xOff++) { for (int yOff = -1; yOff < 2; yOff++) { // Own index // Neighbour index int x = GetGridIndexX(ref particle) + xOff; int y = GetGridIndexY(ref particle) + yOff; // Clamp if (x > -1 && x < this.Width && y > -1 && y < this.Height) { if (m_grid[x] != null) { ArrayList idxList = m_grid[x][y]; if (idxList != null) { // Return neighbours index neighbours = (ArrayList)idxList.Clone(); return; } } } } } }
public void Refresh(ref ArrayList particles) { m_grid = null; m_grid = new ArrayList[Width][]; if (particles != null) { for (int i = 0; i < particles.Count; i++) { FluidParticle p = (FluidParticle)particles[i]; int gridIndexX = GetGridIndexX(ref p); int gridIndexY = GetGridIndexY(ref p); if (m_grid[gridIndexX] == null) { m_grid[gridIndexX] = new ArrayList[Height]; } if (m_grid[gridIndexX][gridIndexY] == null) { m_grid[gridIndexX][gridIndexY] = new ArrayList(); } m_grid[gridIndexX][gridIndexY].Add(i); } } }
public void ShootFluidParticle(Transform transform) { FluidParticle p = new FluidParticle( transform.TransformPoint(new Vector3(0, 0, 0.5f)) + new Vector3(0, 0.5f, 0), transform.forward * ParticleVelocityScaling, random); newParticles.Add(p); }
private float DerivativeCSum(FluidParticle p, List <FluidParticle> neigbours) { float sum = 0; foreach (FluidParticle neighbour in neigbours) { // Use the derivative of the spiky kernel. float derivative = SpikyDerivative(p, neighbour); sum += Mathf.Pow(derivative, 2); } return(sum + RelaxationConstant); }
private float StdSPHDensityEstimator(FluidParticle p, List <FluidParticle> neighbours) { float pi = 0; foreach (FluidParticle neighbour in neighbours) { // Use the Poly6 kernel pi += (315f / 64 * Mathf.PI * Mathf.Pow(NeighbourDistance, 4)) * Mathf.Pow(Mathf.Pow(NeighbourDistance, 2) - Mathf.Pow(Vector3.Distance(p.PredictedPosition, neighbour.PredictedPosition), 2), 3); } return(pi); }
void Init() { switch (ParticleNum) { case NumParticlesSet.NUM_8K: _numParticles = NUM_PARTICLES_8K; break; case NumParticlesSet.NUM_16K: _numParticles = NUM_PARTICLES_16K; break; case NumParticlesSet.NUM_32K: _numParticles = NUM_PARTICLES_32K; break; case NumParticlesSet.NUM_64K: _numParticles = NUM_PARTICLES_64K; break; default: _numParticles = 64; break; } Debug.Log("numParticles : " + _numParticles); _particlesBuffer = new ComputeBuffer(_numParticles, Marshal.SizeOf(typeof(FluidParticle))); int startingWidth = (int)Mathf.Sqrt((float)_numParticles); var particles = new FluidParticle[_numParticles]; for (int i = 0; i < _numParticles; i++) { particles[i].Velocity = Vector3.zero; particles[i].Position = _containerCenter + Random.insideUnitSphere * ballRadius; } _particlesBuffer.SetData(particles); _sortedParticlesBuffer = new ComputeBuffer(_numParticles, Marshal.SizeOf(typeof(FluidParticle))); _particleForcesBuffer = new ComputeBuffer(_numParticles, Marshal.SizeOf(typeof(FluidParticleForces))); _particleDensityBuffer = new ComputeBuffer(_numParticles, Marshal.SizeOf(typeof(FluidParticleDensity))); _gridBuffer = new ComputeBuffer(_numParticles, Marshal.SizeOf(typeof(Uint2))); _gridPingPongBuffer = new ComputeBuffer(_numParticles, Marshal.SizeOf(typeof(Uint2))); _gridIndicesBuffer = new ComputeBuffer(NUM_GRID_INDICES, Marshal.SizeOf(typeof(Uint2))); particles = null; }
private int GetGridIndexY(ref FluidParticle particle) { int gridIndexY = (int)(particle.Position.y / CellSpace); // Clamp Y if (gridIndexY < 0) { gridIndexY = 0; } if (gridIndexY >= Height) { gridIndexY = Height - 1; } return(gridIndexY); }
private int GetGridIndexX(ref FluidParticle particle) { int gridIndexX = (int)(particle.Position.x / CellSpace); // Clamp X if (gridIndexX < 0) { gridIndexX = 0; } if (gridIndexX >= Width) { gridIndexX = Width - 1; } return(gridIndexX); }
/*Calculate the power of a point only if it hasn't been calculated already for this frame*/ public float i() { float pwr; if (cntr < mcblob.pctr) { cntr = mcblob.pctr; pwr = 0f; for (int jc = 0; jc < this.mcblob.GetComponent <FluidBehaviour>().Particles.Count; jc++) { FluidParticle pb = this.mcblob.GetComponent <FluidBehaviour>().Particles[jc]; pwr += (1.0f / Mathf.Sqrt(((pb.Position.x - this.x) * (pb.Position.x - this.x)) + ((pb.Position.y - this.y) * (pb.Position.y - this.y)) + ((pb.Position.z - this.z) * (pb.Position.z - this.z)))) * MCBlob.POWER; } this._i = pwr; } return(this._i); }
private void UpdateParticles(ref ArrayList particles, float dTime) { float r = Domain.xMax; float l = Domain.x; float t = Domain.yMax; float b = Domain.y; for (int i = 0; i < particles.Count; i++) { FluidParticle particle = (FluidParticle)particles[i]; if (particle.Position.x < l) { particle.Position.x = l + Mathf.Epsilon; } else if (particle.Position.x > r) { particle.Position.x = r - Mathf.Epsilon; } if (particle.Position.y < b) { particle.Position.y = b + Mathf.Epsilon; } else if (particle.Position.y > t) { particle.Position.y = t - Mathf.Epsilon; } if (particle.Position.z < -1) { particle.Position.z = particle.Position.z + 0.0005f; } else if (particle.Position.z > 1.5) { particle.Position.z = particle.Position.z - 0.0005f; } // Update velocity + position using forces particle.Update(dTime); // Reset force particle.Force = Vector2.zero; } }
private void CheckParticleDistance(ref ArrayList particles, ref IndexGrid grid) { float minDist = 0.5f * CellSpace; float minDistSq = minDist * minDist; Vector3 dist; for (int i = 0; i < particles.Count; i++) { FluidParticle p = (FluidParticle)particles[i]; foreach (int nIdx in grid.GetNeighbourIndex(p)) { FluidParticle pn = (FluidParticle)particles[nIdx]; if (p != pn) { dist = pn.Position - p.Position; float distLenSq = dist.sqrMagnitude; if (distLenSq < minDistSq) { if (distLenSq > Mathf.Epsilon) { float distLen = (float)Math.Sqrt((double)distLenSq); dist = dist * 0.5f * (distLen - minDist) / distLen; pn.Position = pn.Position - dist; pn.PositionOld = pn.PositionOld - dist; p.Position = p.Position + dist; p.PositionOld = p.PositionOld + dist; } else { float diff = 0.5f * minDist; pn.Position.x -= diff; pn.Position.y -= diff; p.Position.x += diff; p.PositionOld.y += diff; } } } } } }
void InitResources() { _particlesBufferRead = new ComputeBuffer((int)_numParticles, Marshal.SizeOf(typeof(FluidParticle))); _particlesBufferWrite = new ComputeBuffer((int)_numParticles, Marshal.SizeOf(typeof(FluidParticle))); _particlesForceBuffer = new ComputeBuffer((int)_numParticles, Marshal.SizeOf(typeof(FluidParticleForces))); _particlesDensityBuffer = new ComputeBuffer((int)_numParticles, Marshal.SizeOf(typeof(FluidParticleDensity))); FluidParticle[] particles = new FluidParticle[_numParticles]; for (var i = 0; i < _numParticles; i++) { particles[i].Position = Random.insideUnitSphere * 0.01f; particles[i].Velocity = Vector3.zero; } _particlesBufferRead.SetData(particles); _particlesBufferWrite.SetData(particles); particles = null; }
/* Normals are calculated by 'averaging' all the derivatives of the Blob power functions*/ private Vector3 calcNormal(Vector3 pnt) { int jc; Vector3 result = tada[tadac++]; result.x = 0; result.y = 0; result.z = 0; for (jc = 0; jc < this.GetComponent <FluidBehaviour>().Particles.Count; jc++) { FluidParticle pb = this.GetComponent <FluidBehaviour>().Particles[jc]; Vector3 current = tada[tadac++]; current.x = pnt.x - pb.Position.x; current.y = pnt.y - pb.Position.y; current.z = pnt.z - pb.Position.z; float mag = current.magnitude; float pwr = .5f * (1f / (mag * mag * mag)) * MCBlob.POWER; result = result + (current * pwr); } return(result.normalized); }
void OnPreSolve(FluvioTimeStep timeStep) { if (m_Rigidbodies == null) { return; } int l = Mathf.Min(m_Rigidbodies.Length, fluid.particleCount); for (int i = 0; i < l; ++i) { // Get objects Rigidbody2D body = m_Rigidbodies[i]; CircleCollider2D collider = m_Colliders[i]; FluidParticle particle = fluid.GetParticle(i); // Assign properties body.gameObject.SetActive(particle.enabled); if (body.isKinematic) { body.isKinematic = false; } body.mass = fluid.particleMass * m_MassModifier; body.interpolation = m_RigidbodyInterpolation; collider.radius = fluid.smoothingDistance * m_ColliderSizeModifier; collider.sharedMaterial = m_PhysicMaterial; if (particle.lifetime < 1.0f) { // Send physics information to particles (collisions) particle.position = body.transform.position; particle.velocity = body.velocity * timeStep.dt; // Assign particle fluid.SetParticle(ref particle, i); } } }
void OnPostSolve(FluvioTimeStep timeStep) { if (m_Rigidbodies == null) { return; } int l = Mathf.Min(m_Rigidbodies.Length, fluid.particleCount); for (int i = 0; i < l; ++i) { // Get objects Rigidbody2D body = m_Rigidbodies[i]; FluidParticle particle = fluid.GetParticle(i); if (body.isKinematic) { body.isKinematic = false; } body.transform.position = particle.position; body.velocity = particle.velocity * timeStep.inv_dt; } }
public void Emit(ref ArrayList particles, double dTime) { if (this.Enabled) { m_time += dTime; int nParts = (int)(this.Frequency * m_time); if (nParts > 0) { for (int i = 0; i < nParts; i++) { float dist = UnityEngine.Random.value * Distribution - Distribution * 0.5f; Vector3 normal = new Vector3(Direction.y, -Direction.x, Direction.z); normal = normal * dist; Vector3 vel = Direction + normal; Vector3 vel3 = new Vector3(vel.x, vel.z, vel.y); vel3.Normalize(); vel = new Vector3(vel3.x, vel3.y, vel3.z); float velLen = UnityEngine.Random.value * (this.VelocityMax - this.VelocityMin) + this.VelocityMin; vel = vel * velLen; Vector3 oldPos = this.Position - vel * (float)m_time; FluidParticle f = new FluidParticle(); f.Position = Position; f.PositionOld = oldPos; f.Velocity = vel; f.Mass = ParticleMass; particles.Add(f); } m_time = 0.0; } } }
private void CalculateForces(ref ArrayList particles, ref IndexGrid grid, Vector3 globalForce) { Vector3 f, dist; float scalar; for (int i = 0; i < particles.Count; i++) { FluidParticle p = (FluidParticle)particles[i]; p.Force += globalForce; foreach (int nIdx in grid.GetNeighbourIndex(p)) { if (nIdx < i) { FluidParticle pn = (FluidParticle)particles[nIdx]; if (pn.Density > Mathf.Epsilon && p != pn) { dist = p.Position - pn.Position; scalar = pn.Mass * (p.Pressure + pn.Pressure) / (2.0f * pn.Density); f = SKPressure.CalculateGradient(ref dist); f = f * scalar; p.Force -= f; pn.Force += f; scalar = pn.Mass * (float)this.SKViscosity.CalculateLaplacian(ref dist) * Viscosity * 1 / pn.Density; f = pn.Velocity - p.Velocity; f = f * scalar; p.Force += f; pn.Force -= f; } } } } }
void OnPostSolve(FluvioTimeStep timeStep) { if (sleep) { sleep = false; return; } fluid.GetParticles(ref particles); #if !(UNITY_IPHONE || UNITY_ANDROID) || UNITY_EDITOR bool doTouch = !requireClick ? true : (Input.GetMouseButton(0) || Input.GetMouseButton(1)); if (doTouch) { if (Input.GetMouseButton(0) || !requireClick) { touchMode = invert ? TouchMode.Push : TouchMode.Pull; } else { touchMode = invert ? TouchMode.Pull : TouchMode.Push; } Vector3 point = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, z)); float f = (int)touchMode * force; for (int i = 0; i < particles.Length; i++) { FluidParticle p = particles[i]; if (!p.enabled) { continue; } float dist = (p.position - point).sqrMagnitude; if (dist <= maxDistance * maxDistance) { p.AddForce((point - p.position).normalized * f); } particles[i] = p; } } #else foreach (Touch t in Input.touches) { Vector3 point = Camera.main.ScreenToWorldPoint(new Vector3(t.position.x, t.position.y, z)); float f = force * (int)touchMode / (Input.touchCount * .5f); for (int i = 0; i < particles.Length; i++) { FluidParticle p = particles[i]; if (!p.enabled) { continue; } float dist = (p.position - point).sqrMagnitude; if (dist <= maxDistance * maxDistance) { p.AddForce((point - p.position).normalized * f); } particles[i] = p; } } #endif fluid.SetParticles(particles, particles.Length); }
private float SpikyDerivative(FluidParticle p1, FluidParticle p2) { return(45 * Mathf.Pow(NeighbourDistance, 6) / Mathf.PI * Mathf.Pow(NeighbourDistance - Vector3.Distance(p1.PredictedPosition, p2.PredictedPosition), 2)); }
private float CalculateViscosity(FluidParticle particle, float f) { throw new NotImplementedException(); }
void FixedUpdate() { // Remove all particles that have fallen too low, have expired or hit fire if (RemoveExpiredParticles) { Particles.RemoveAll(p => p.ToBeRemoved || (p.Position.y < -1) || p.Expiry <= 0); } else { Particles.RemoveAll(p => p.ToBeRemoved || (p.Position.y < -1)); } // Add new particles Particles.AddRange(newParticles); newParticles.Clear(); int newParticleCount = Particles.Count; if (newParticleCount != particleCount) { particleCount = newParticleCount; Debug.Log(particleCount + " fluid particles."); } // Predict the positions of the particles // Lines 1-4 from the algorithm in the paper foreach (FluidParticle p in Particles) { // Apply gravity p.Velocity -= Gravity; Vector3 newPosition = p.Position + p.Velocity; RaycastHit hit = new RaycastHit(); if (Physics.Linecast(p.Position, newPosition + ParticleDiameter * (newPosition - p.Position).normalized, out hit)) { if (hit.normal.y != 0) { p.Velocity += Gravity; } p.Velocity = Vector3.Reflect(p.Velocity * ParticleElasticity, hit.normal); newPosition = p.Position + p.Velocity; FireCell fireCell = hit.collider.GetComponent <FireCell>(); if (fireCell != null) { fireCell.Water(); p.ToBeRemoved = true; } } p.PredictedPosition = newPosition; } // Find neighbours of each particle // Lines 5-7 from the algorithm in the paper List <FluidParticle>[] neighbours = new List <FluidParticle> [Particles.Count]; for (int i = 0; i < Particles.Count; i++) { FluidParticle p = Particles[i]; p.Index = i; List <FluidParticle> foundNeighbours = new List <FluidParticle>(); foreach (FluidParticle potentialNeighbour in Particles) { if (potentialNeighbour != p && Vector3.Distance(potentialNeighbour.Position, p.Position) < NeighbourDistance) { foundNeighbours.Add(potentialNeighbour); } } neighbours[i] = foundNeighbours; } float[] lambdas = new float[Particles.Count]; Vector3[] deltaPos = new Vector3[Particles.Count]; // Refine the predicted positions by taking incompressibilty into account // Lines 8-19 from the algorithm in the paper for (int iteration = 0; iteration < SolverIterations; iteration++) { // Find lambda for each particle // Lines 9-11 from the algorithm in the paper for (int i = 0; i < Particles.Count; i++) { FluidParticle p = Particles[i]; List <FluidParticle> foundNeighbours = neighbours[i]; float pi = StdSPHDensityEstimator(p, foundNeighbours); float c = pi / RestDensity - 1; float deltaCSum = DerivativeCSum(p, foundNeighbours); lambdas[i] = -c / deltaCSum; } // Find more finely predicted positions // Lines 12-15 from the algorithm in the paper for (int i = 0; i < Particles.Count; i++) { FluidParticle p = Particles[i]; List <FluidParticle> foundNeighbours = neighbours[i]; deltaPos[i] = Vector3.zero; foreach (FluidParticle neigbour in foundNeighbours) { deltaPos[i] += (lambdas[i] + lambdas[neigbour.Index]) * Vector3.Normalize(p.PredictedPosition - neigbour.Position) * SpikyDerivative(p, neigbour); } deltaPos[i] = 1 / RestDensity * deltaPos[i]; Vector3 newPosition = p.PredictedPosition + deltaPos[i]; if (Physics.Linecast(p.PredictedPosition, newPosition + ParticleDiameter * (newPosition - p.Position).normalized)) { deltaPos[i] = Vector3.zero; } } // Use the new positions // Lines 16-18 from the algorithm in the paper for (int i = 0; i < Particles.Count; i++) { FluidParticle p = Particles[i]; p.PredictedPosition += deltaPos[i]; } } // Actually set the new positions and count down the expiry of the particles. foreach (FluidParticle p in Particles) { p.Velocity = p.PredictedPosition - p.Position; p.Position = p.PredictedPosition; p.Expiry--; } }