Example #1
0
        /// <summary>
        /// Checks the distance between the particles and corrects it, if they are to near.
        /// </summary>
        /// <param name="particles">The particles.</param>
        /// <param name="grid">The grid.</param>
        private void CheckParticleDistance(FluidParticles particles, IndexGrid grid)
        {
            float   minDist   = 0.5f * CellSpace;
            float   minDistSq = minDist * minDist;
            Vector2 dist;

            for (int i = 0; i < particles.Count; i++)
            {
                foreach (var nIdx in grid.GetNeighbourIndex(particles[i]))
                {
                    Vector2.Sub(ref particles[nIdx].Position, ref particles[i].Position, out dist);
                    float distLenSq = dist.LengthSquared;
                    if (distLenSq < minDistSq)
                    {
                        if (distLenSq > Constants.FLOAT_EPSILON)
                        {
                            float distLen = (float)Math.Sqrt((double)distLenSq);
                            Vector2.Mult(ref dist, 0.5f * (distLen - minDist) / distLen, out dist);
                            Vector2.Sub(ref particles[nIdx].Position, ref dist, out particles[nIdx].Position);
                            Vector2.Sub(ref particles[nIdx].PositionOld, ref dist, out particles[nIdx].PositionOld);
                            Vector2.Add(ref particles[i].Position, ref dist, out particles[i].Position);
                            Vector2.Add(ref particles[i].PositionOld, ref dist, out particles[i].PositionOld);
                        }
                        else
                        {
                            float diff = 0.5f * minDist;
                            particles[nIdx].Position.Y    -= diff;
                            particles[nIdx].PositionOld.Y -= diff;
                            particles[i].Position.Y       += diff;
                            particles[i].PositionOld.Y    += diff;
                        }
                    }
                }
            }
        }
Example #2
0
        /// <summary>
        /// Create particles evenly spaced on ground of the boundary.
        /// </summary>
        public static FluidParticles Create(int nParticles, float cellSpace, RectangleF domain, float particleMass)
        {
            FluidParticles particles = new FluidParticles(nParticles);

            // Init. Particle positions
            float x0 = domain.X + cellSpace;
            float x  = x0;
            float y  = domain.Y;

            for (int i = 0; i < nParticles; i++)
            {
                if (x == x0)
                {
                    y += cellSpace;
                }
                Vector2 pos = new Vector2(x, y);
                particles.Add(new FluidParticle
                {
                    Position    = pos,
                    PositionOld = pos,
                    Mass        = particleMass,
                });
                x = x + cellSpace < domain.Width ? x + cellSpace : x0;
            }

            return(particles);
        }
Example #3
0
        /// <summary>
        /// Updates the particles posotions using integration and clips them to the domain space.
        /// </summary>
        /// <param name="particles">The particles.</param>
        /// <param name="dTime">The time step.</param>
        private void UpdateParticles(FluidParticles particles, float dTime)
        {
            float r = this.Domain.Right;
            float l = this.Domain.X;
            // Rectangle contains coordinates inverse on y
            float t = this.Domain.Bottom;
            float b = this.Domain.Y;

            foreach (var particle in particles)
            {
                // Clip positions to domain space
                if (particle.Position.X < l)
                {
                    particle.Position.X = l + Constants.FLOAT_EPSILON;
                }
                else if (particle.Position.X > r)
                {
                    particle.Position.X = r - Constants.FLOAT_EPSILON;
                }
                if (particle.Position.Y < b)
                {
                    particle.Position.Y = b + Constants.FLOAT_EPSILON;
                }
                else if (particle.Position.Y > t)
                {
                    particle.Position.Y = t - Constants.FLOAT_EPSILON;
                }

                // Update velocity + position using forces
                particle.Update(dTime);
                // Reset force
                particle.Force = Vector2.Zero;
            }
        }
Example #4
0
 /// <summary>
 /// Simulates the specified particles.
 /// </summary>
 /// <param name="particles">The particles.</param>
 /// <param name="globalForce">The global force.</param>
 /// <param name="dTime">The time step.</param>
 public void Calculate(FluidParticles particles, Vector2 globalForce, float dTime)
 {
     m_grid.Refresh(particles);
     CalculatePressureAndDensities(particles, m_grid);
     CalculateForces(particles, m_grid, globalForce);
     UpdateParticles(particles, dTime);
     CheckParticleDistance(particles, m_grid);
 }
Example #5
0
        public IndexGrid(float cellSpace, RectangleF domain, FluidParticles particles)
        {
            this.CellSpace = cellSpace;
            this.Domain    = domain;
            this.Width     = (int)(this.Domain.Width / this.CellSpace);
            this.Height    = (int)(this.Domain.Height / this.CellSpace);

            this.Refresh(particles);
        }
Example #6
0
 /// <summary>
 /// Renders the specified particles using the size of the texture (specified in ctor).
 /// </summary>
 /// <param name="particles">The particles.</param>
 /// <param name="domain">The source rectangle (min, max of particle position).</param>
 public void Render(FluidParticles particles, RectangleF domain)
 {
     Vector2[] destCoordinates = new Vector2[]
     {
         new Vector2(domain.Left, domain.Bottom),
         new Vector2(domain.Right, domain.Bottom),
         new Vector2(domain.Right, domain.Top),
         new Vector2(domain.Left, domain.Top)
     };
     this.Render(particles, domain, destCoordinates);
 }
Example #7
0
        /// <summary>
        /// Renders the specified particles.
        /// </summary>
        /// <param name="particles">The particles.</param>
        /// <param name="domain">The source rectangle (min, max of possible particle position).</param>
        /// <param name="destCoordinates">The destination coordinates, lenght of the array has to be 4.
        /// Index 0: upper left corner.
        /// Index 1: upper right corner.
        /// Index 2: lower right corner.
        /// Index 3: lower left corner</param>
        public void Render(FluidParticles particles, RectangleF domain, Vector2[] destCoordinates)
        {
            if (destCoordinates.Length != 4)
            {
                throw new ArgumentOutOfRangeException("destCoordinates.Length != 4");
            }

            BeginDraw();

            //
            // Render-To-Texture
            //

            RenderToTex(particles, domain);

            //
            // Render-To-Framebuffer
            //

            //// Clear Frame-Buffer
            //if (m_fboRender == 0)
            //{
            //   GL.Clear(ClearBufferMask.ColorBufferBit);
            //}

            // AlphaTest checks if the energy (alpha) is greater or equal than the threshold
            GL.Enable(EnableCap.AlphaTest);

            // Draw single blue quad (background)
            GL.Disable(EnableCap.Texture2D);
            GL.Color4(Color);
            GL.Begin(BeginMode.Quads);
            GL.Vertex2(destCoordinates[0].X, destCoordinates[0].Y);
            GL.Vertex2(destCoordinates[1].X, destCoordinates[1].Y);
            GL.Vertex2(destCoordinates[2].X, destCoordinates[2].Y);
            GL.Vertex2(destCoordinates[3].X, destCoordinates[3].Y);
            GL.End();

            // Draw single quad with rendered alpha texture
            GL.Enable(EnableCap.Texture2D);
            BindRenderTexture();
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0.0f, 1.0f);
            GL.Vertex2(destCoordinates[0].X, destCoordinates[0].Y);
            GL.TexCoord2(1.0f, 1.0f);
            GL.Vertex2(destCoordinates[1].X, destCoordinates[1].Y);
            GL.TexCoord2(1.0f, 0.0f);
            GL.Vertex2(destCoordinates[2].X, destCoordinates[2].Y);
            GL.TexCoord2(0.0f, 0.0f);
            GL.Vertex2(destCoordinates[3].X, destCoordinates[3].Y);
            GL.End();

            EndDraw();
        }
Example #8
0
        /// <summary>
        /// Renders to texture.
        /// </summary>
        /// <param name="particles">The particles.</param>
        /// <param name="domain">The source rectangle (min, max of particle position).</param>

        private void RenderToTex(FluidParticles particles, RectangleF domain)
        {
            //
            // Render-To-Texture
            //

            // Bind FBO if available and clear it
            if (m_fboRender != 0)
            {
                GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, m_fboRender);
                GL.Clear(ClearBufferMask.ColorBufferBit);
            }
            // Set correct viewport
            GL.PushAttrib(AttribMask.ViewportBit);
            GL.Viewport(0, 0, m_texSize, m_texSize);

            GL.Disable(EnableCap.AlphaTest);
            GL.Enable(EnableCap.Texture2D);
            GL.BindTexture(TextureTarget.Texture2D, m_texAttentuation);

            // Trasnform radius from screen to domain
            float rat  = m_radiusSquared / m_texSize;
            float radW = rat * domain.Width;
            float radH = rat * domain.Height;

            // Draw particles as quads with the size of the squared radius
            GL.Begin(BeginMode.Quads);
            foreach (var particle in particles)
            {
                GL.TexCoord2(0.0f, 0.0f);
                GL.Vertex2(particle.Position.X - radW, particle.Position.Y - radH);
                GL.TexCoord2(1.0f, 0.0f);
                GL.Vertex2(particle.Position.X + radW, particle.Position.Y - radH);
                GL.TexCoord2(1.0f, 1.0f);
                GL.Vertex2(particle.Position.X + radW, particle.Position.Y + radH);
                GL.TexCoord2(0.0f, 1.0f);
                GL.Vertex2(particle.Position.X - radW, particle.Position.Y + radH);
            }
            GL.End();

            // Copy framebuffer to texture if fbo isn't available
            if (m_fboRender == 0)
            {
                BindRenderTexture();
                GL.CopyTexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, 0, 0, m_texSize, m_texSize, 0);
            }
            else
            {
                GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
            }

            GL.PopAttrib();
        }
Example #9
0
 /// <summary>
 /// Consumes the specified particles if they are in in the radius.
 /// </summary>
 /// <param name="particles">The particles.</param>
 public void Consume(FluidParticles particles)
 {
     if (this.Enabled)
     {
         for (int i = particles.Count - 1; i >= 0; i--)
         {
             float distSq = (particles[i].Position - this.Position).LengthSquared;
             if (distSq < m_radiusSquared)
             {
                 particles.RemoveAt(i);
             }
         }
     }
 }
Example #10
0
        /// <summary>
        /// Updates the particles (remove, emit, ...).
        /// </summary>
        /// <param name="dTime">The delta time.</param>
        public FluidParticles Update(double dTime)
        {
            FluidParticles emitted = null;

            // Consume particles in a certain range
            if (this.HasConsumers)
            {
                foreach (var consumer in this.Consumers)
                {
                    consumer.Consume(this.Particles);
                }
            }

            // Remove old particles
            if (this.TestMaxLife)
            {
                for (int i = this.Particles.Count - 1; i >= 0; i--)
                {
                    if (this.Particles[i].Life >= this.MaxLife)
                    {
                        this.Particles.RemoveAt(i);
                    }
                }
            }

            // Check if emit is allowed
            if (m_wasMaxReached && !this.DoRebirth)
            {
                // NOP
            }
            else if (this.Particles.Count < this.MaxParticles)
            {
                if (this.HasEmitters)
                {
                    // Emit new particles
                    foreach (var emitter in this.Emitters)
                    {
                        emitted = emitter.Emit(dTime);
                        this.Particles.AddRange(emitted);
                    }
                }
            }
            else
            {
                m_wasMaxReached = true;
            }

            return(emitted);
        }
Example #11
0
        /// <summary>
        /// Calculates the pressure and densities.
        /// </summary>
        /// <param name="particles">The particles.</param>
        /// <param name="grid">The grid.</param>
        private void CalculatePressureAndDensities(FluidParticles particles, IndexGrid grid)
        {
            Vector2 dist;

            foreach (var particle in particles)
            {
                particle.Density = 0.0f;
                foreach (var nIdx in grid.GetNeighbourIndex(particle))
                {
                    Vector2.Sub(ref particle.Position, ref particles[nIdx].Position, out dist);
                    particle.Density += particle.Mass * this.SKGeneral.Calculate(ref dist);
                }
                particle.UpdatePressure();
            }
        }
Example #12
0
        /// <summary>
        /// Solves collisions only for the particles and the bounding volumes associated with this instance.
        /// </summary>
        /// <param name="particles">The particles.</param>
        /// <returns>True, if a collision occured.</returns>
        public bool Solve(FluidParticles particles)
        {
            bool    hasCollided = false;
            Vector2 penetration, penNormal, v, vn, vt;
            float   penLen, dp;

            foreach (var bv in this.BoundingVolumes)
            {
                foreach (var particle in particles)
                {
                    if (bv.Intersects(particle.BoundingVolume, out penNormal, out penLen))
                    {
                        hasCollided = true;
                        Vector2.Mult(ref penNormal, penLen, out penetration);
                        if (particle.BoundingVolume.IsFixed)
                        {
                            bv.Position += penetration;
                        }
                        else
                        {
                            particle.BoundingVolume.Position -= penetration;


                            // Calc new velocity using elastic collision with friction
                            // -> Split oldVelocity in normal and tangential component, revert normal component and add it afterwards
                            // v = pos - oldPos;
                            //vn = n * Vector2.Dot(v, n) * -Bounciness;
                            //vt = t * Vector2.Dot(v, t) * (1.0f - Friction);
                            //v = vn + vt;
                            //oldPos = pos - v;

                            Vector2.Sub(ref particle.Position, ref particle.PositionOld, out v);
                            Vector2 tangent = penNormal.PerpendicularRight;
                            dp = Vector2.Dot(v, penNormal);
                            Vector2.Mult(ref penNormal, dp * -this.Bounciness, out vn);
                            dp = Vector2.Dot(v, tangent);
                            Vector2.Mult(ref tangent, dp * (1.0f - this.Friction), out vt);
                            Vector2.Add(ref vn, ref vt, out v);
                            particle.Position -= penetration;
                            Vector2.Sub(ref particle.Position, ref v, out particle.PositionOld);
                        }
                    }
                }
            }
            return(hasCollided);
        }
Example #13
0
        public void Refresh(FluidParticles particles)
        {
            m_grid = new List <int> [this.Width, this.Height];

            if (particles != null)
            {
                for (int i = 0; i < particles.Count; i++)
                {
                    int gridIndexX = GetGridIndexX(particles[i]);
                    int gridIndexY = GetGridIndexY(particles[i]);

                    // Add particle to list
                    if (m_grid[gridIndexX, gridIndexY] == null)
                    {
                        m_grid[gridIndexX, gridIndexY] = new List <int>();
                    }
                    m_grid[gridIndexX, gridIndexY].Add(i);
                }
            }
        }
Example #14
0
        /// <summary>
        /// Emits particles.
        /// </summary>
        /// <param name="dTime">The delta time.</param>
        /// <returns></returns>
        public FluidParticles Emit(double dTime)
        {
            FluidParticles particles = new FluidParticles();

            if (this.Enabled)
            {
                // Calc particle count based on frequency
                m_time += dTime;
                int nParts = (int)(this.Frequency * m_time);
                if (nParts > 0)
                {
                    // Create Particles
                    for (int i = 0; i < nParts; i++)
                    {
                        // Calc velocity based on the distribution along the normalized direction
                        float   dist   = (float)m_randGen.NextDouble() * this.Distribution - this.Distribution * 0.5f;
                        Vector2 normal = this.Direction.PerpendicularRight;
                        Vector2.Mult(ref normal, dist, out normal);
                        Vector2 vel = this.Direction + normal;
                        vel.Normalize();
                        float velLen = (float)m_randGen.NextDouble() * (this.VelocityMax - this.VelocityMin) + this.VelocityMin;
                        Vector2.Mult(ref vel, velLen, out vel);

                        // Calc Oldpos (for right velocity) using simple euler
                        // oldPos = this.Position - vel * m_time;
                        Vector2 oldPos = this.Position - vel * (float)m_time;

                        particles.Add(new FluidParticle
                        {
                            Position    = this.Position,
                            PositionOld = oldPos,
                            Velocity    = vel,
                            Mass        = this.ParticleMass
                        });
                    }
                    // Reset time
                    m_time = 0.0;
                }
            }
            return(particles);
        }
Example #15
0
        /// <summary>
        /// Calculates the pressure and viscosity forces.
        /// </summary>
        /// <param name="particles">The particles.</param>
        /// <param name="grid">The grid.</param>
        /// <param name="globalForce">The global force.</param>
        private void CalculateForces(FluidParticles particles, IndexGrid grid, Vector2 globalForce)
        {
            Vector2 f, dist;
            float   scalar;

            for (int i = 0; i < particles.Count; i++)
            {
                // Add global force to every particle
                particles[i].Force += globalForce;

                foreach (var nIdx in grid.GetNeighbourIndex(particles[i]))
                {
                    // Prevent double tests
                    if (nIdx < i)
                    {
                        if (particles[nIdx].Density > Constants.FLOAT_EPSILON)
                        {
                            Vector2.Sub(ref particles[i].Position, ref particles[nIdx].Position, out dist);

                            // pressure
                            // f = particles[nIdx].Mass * ((particles[i].Pressure + particles[nIdx].Pressure) / (2.0f * particles[nIdx].Density)) * WSpikyGrad(ref dist);
                            scalar = particles[nIdx].Mass * (particles[i].Pressure + particles[nIdx].Pressure) / (2.0f * particles[nIdx].Density);
                            f      = this.SKPressure.CalculateGradient(ref dist);
                            Vector2.Mult(ref f, scalar, out f);
                            particles[i].Force    -= f;
                            particles[nIdx].Force += f;

                            // viscosity
                            // f = particles[nIdx].Mass * ((particles[nIdx].Velocity - particles[i].Velocity) / particles[nIdx].Density) * WViscosityLap(ref dist) * Constants.VISC0SITY;
                            scalar = particles[nIdx].Mass * this.SKViscosity.CalculateLaplacian(ref dist) * this.Viscosity * 1 / particles[nIdx].Density;
                            f      = particles[nIdx].Velocity - particles[i].Velocity;
                            Vector2.Mult(ref f, scalar, out f);
                            particles[i].Force    += f;
                            particles[nIdx].Force -= f;
                        }
                    }
                }
            }
        }