Commits

Juha Komulainen  committed 2dfc6ec

Added support for particle links and constraints. Added bridge demo.

  • Participants
  • Parent commits 8b0c997

Comments (0)

Files changed (8)

File CycloNet.Physics.Demos/Bridge/BridgeDemo.cs

+using System;
+using System.Linq;
+using System.Collections.Generic;
+using CycloNet.Physics.Particles;
+using OpenTK;
+using OpenTK.Graphics.OpenGL;
+using Tao.FreeGlut;
+
+namespace CycloNet.Physics.Demos.Bridge
+{
+    public class BridgeDemo : MassAggregateApplication
+    {
+        const int RodCount = 6;
+        const int CableCount = 10;
+        const int SupportCount = 12;
+        const float BaseMass = 1;
+        const float ExtraMass = 10;
+
+        List<ParticleCableConstraint> supports = new List<ParticleCableConstraint>(SupportCount);
+        List<ParticleCable> cables = new List<ParticleCable>(CableCount);
+        List<ParticleRod> rods = new List<ParticleRod>(RodCount);
+
+        Vector3 MassPos = new Vector3(0, 0, 0.5f);
+        Vector3 MassDisplayPos;
+
+        public BridgeDemo():
+            base(12)
+        {
+            Title = "CycloNet - Bridge Demo";
+
+            // Create masses
+            for (int i = 0; i < Particles.Count; i++)
+            {
+                var p = Particles[i];
+
+                p.Position = new Vector3((i/2)*2.0f-5.0f, 4, (i%2)*2.0f-1.0f);
+                p.Velocity = Vector3.Zero;
+                p.Damping = 0.9f;
+                p.Acceleration = Gravity;
+                p.ClearAccumulator();
+            }
+
+            // Add the links
+            for (int i = 0; i < CableCount; i++)
+            {
+                cables.Add(new ParticleCable(Particles[i], Particles[i+2])
+                {
+                    MaxLength = 1.9f,
+                    Restitution = 0.3f
+                });
+            }
+
+            for (int i = 0; i < SupportCount; i++)
+            {
+                var anchor = new Vector3((i/2)*2.2f-5.5f, 6, (i%2)*1.6f-0.8f);
+                supports.Add(new ParticleCableConstraint(Particles[i], anchor)
+                {
+                    MaxLength = (i <  6) ? ((i/2)*0.5f + 3.0f) : (5.5f - (i/2)*0.5f),
+                    Restitution = 0.5f
+                });
+            }
+
+            for (int i = 0; i < RodCount; i++)
+            {
+                rods.Add(new ParticleRod(Particles[i*2], Particles[i*2+1])
+                {
+                    Length = 2
+                });
+            }
+
+            World.ContactGenerators.AddRange(cables);
+            World.ContactGenerators.AddRange(supports);
+            World.ContactGenerators.AddRange(rods);
+
+            UpdateAdditionalMass();
+        }
+
+        private void UpdateAdditionalMass()
+        {
+            foreach (var p in Particles)
+                p.Mass = BaseMass;
+
+            // Find the coordinates of the mass as an index and proportion
+            var x = (int) MassPos.X;
+            var xp = MassPos.X % 1.0f;
+            if (x < 0)
+            {
+                x = 0;
+                xp = 0;
+            }
+            if (x >= 5)
+            {
+                x = 5;
+                xp = 0;
+            }
+
+            var z = (int) MassPos.Z;
+            var zp = MassPos.Z % 1.0f;
+            if (z < 0)
+            {
+                z = 0;
+                zp = 0;
+            }
+            if (z >= 1)
+            {
+                z = 1;
+                zp = 0;
+            }
+
+            // Calculate where to draw the mass
+            MassDisplayPos = Vector3.Zero;
+
+            // Add the proportion to the correct masses
+            Particles[x*2+z].Mass = BaseMass + ExtraMass*(1-xp)*(1-zp);
+
+            MassDisplayPos += Particles[x*2+z].Position * ((1-xp)*(1-zp));
+
+            if (xp > 0)
+            {
+                Particles[x*2+z+2].Mass = BaseMass + ExtraMass*xp*(1-zp);
+                MassDisplayPos +=
+                    Particles[x*2+z+2].Position * (xp*(1-zp));
+
+                if (zp > 0)
+                {
+                    Particles[x*2+z+3].Mass = BaseMass + ExtraMass*xp*zp;
+                    MassDisplayPos += Particles[x*2+z+3].Position * (xp*zp);
+                }
+            }
+            if (zp > 0)
+            {
+                Particles[x*2+z+1].Mass = BaseMass + ExtraMass*(1-xp)*zp;
+                MassDisplayPos += Particles[x*2+z+1].Position * ((1-xp)*zp);
+            }
+        }
+
+        protected override void DoRender()
+        {
+            base.DoRender();
+
+            GL.Begin(BeginMode.Lines);
+
+            GL.Color3(0, 0, 1.0f);
+            foreach (var rod in rods)
+            {
+                GL.Vertex3(rod.Particle0.Position);
+                GL.Vertex3(rod.Particle1.Position);
+            }
+
+            GL.Color3(0, 1.0f, 0);
+            foreach (var cable in cables)
+            {
+                GL.Vertex3(cable.Particle0.Position);
+                GL.Vertex3(cable.Particle1.Position);
+            }
+
+            GL.Color3(0.7f, 0.7f, 0.7f);
+            foreach (var support in supports)
+            {
+                GL.Vertex3(support.Particle.Position);
+                GL.Vertex3(support.Anchor);
+            }
+
+            GL.End();
+
+            GL.Color3(1.0f, 0, 0);
+            GL.PushMatrix();
+            GL.Translate(MassDisplayPos);
+
+            Glut.glutSolidSphere(0.25f, 20, 10);
+            GL.PopMatrix();
+        }
+
+        protected override void DoUpdate(float elapsed)
+        {
+            base.DoUpdate(elapsed);
+
+            UpdateAdditionalMass();
+        }
+
+        protected override void OnKeyPress(KeyPressEventArgs e)
+        {
+            switch (e.KeyChar)
+            {
+            case 's': case 'S':
+                MassPos.Z += 0.1f;
+                if (MassPos.Z > 1.0f) MassPos.Z = 1.0f;
+                break;
+            case 'w': case 'W':
+                MassPos.Z -= 0.1f;
+                if (MassPos.Z < 0.0f) MassPos.Z = 0.0f;
+                break;
+            case 'a': case 'A':
+                MassPos.X -= 0.1f;
+                if (MassPos.X < 0.0f) MassPos.X = 0.0f;
+                break;
+            case 'd': case 'D':
+                MassPos.X += 0.1f;
+                if (MassPos.X > 5.0f) MassPos.X = 5.0f;
+                break;
+            }
+        }
+    }
+}
+

File CycloNet.Physics.Demos/CycloNet.Physics.Demos.csproj

     <OutputType>Exe</OutputType>
     <RootNamespace>CycloNet.Physics.Demos</RootNamespace>
     <AssemblyName>CycloNet.Physics.Demos</AssemblyName>
-    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
     <Compile Include="Ballistic\BallisticDemo.cs" />
     <Compile Include="TimingData.cs" />
     <Compile Include="Fireworks\FireworksDemo.cs" />
+    <Compile Include="Bridge\BridgeDemo.cs" />
+    <Compile Include="MassAggregateApplication.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>
   <ItemGroup>
     <Folder Include="Ballistic\" />
     <Folder Include="Fireworks\" />
+    <Folder Include="Bridge\" />
+    <Folder Include="Platform\" />
+    <Folder Include="BigBallistic\" />
+    <Folder Include="Blob\" />
   </ItemGroup>
 </Project>

File CycloNet.Physics.Demos/DemoApplication.cs

 {
     public abstract class DemoApplication : GameWindow
     {
+        public readonly Vector3 Gravity = new Vector3(0, -9.81f, 0);
+        
         public DemoApplication():
             base(800, 600)
         {

File CycloNet.Physics.Demos/Main.cs

     {
         public static void Main(string[] args)
         {
-            using (var win = new Fireworks.FireworksDemo())
             //using (var win = new Ballistic.BallisticDemo())
+            //using (var win = new Fireworks.FireworksDemo())
+            using (var win = new Bridge.BridgeDemo())
             {
                 win.Run();
             }

File CycloNet.Physics.Demos/MassAggregateApplication.cs

+using System;
+using System.Collections.Generic;
+using CycloNet.Physics.Particles;
+using OpenTK;
+using OpenTK.Graphics;
+using OpenTK.Graphics.OpenGL;
+using Tao.FreeGlut;
+
+namespace CycloNet.Physics.Demos
+{
+    public class MassAggregateApplication : DemoApplication
+    {
+        protected readonly ParticleWorld World;
+        protected readonly GroundContacts GroundContactGenerator;
+
+        protected List<Particle> Particles
+        {
+            get { return World.Particles; }
+        }
+
+        public MassAggregateApplication(int particleCount)
+        {
+            World = new ParticleWorld(particleCount * 10);
+
+            for (int i = 0; i < particleCount; i++)
+                World.Particles.Add(new Particle());
+
+            GroundContactGenerator = new GroundContacts(World.Particles);
+            World.ContactGenerators.Add(GroundContactGenerator);
+        }
+
+        protected override void DoRender()
+        {
+            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
+
+            var camera = Matrix4.LookAt(0.0f, 3.5f, 8.0f,  0.0f, 3.5f, 0.0f,  0.0f, 1.0f, 0.0f);
+            GL.LoadMatrix(ref camera);
+
+            GL.Color4(Color4.Black);
+
+            foreach (var particle in World.Particles)
+            {
+                GL.PushMatrix();
+                GL.Translate(particle.Position);
+                Glut.glutSolidSphere(0.1f, 20, 10);
+                GL.PopMatrix();
+            }
+        }
+
+        protected override void DoUpdate(float elapsed)
+        {
+            World.StartFrame();
+            World.RunPhysics(elapsed);
+        }
+    }
+}
+

File CycloNet.Physics/CycloNet.Physics.csproj

     <Compile Include="Particles\ParticleForceGenerators.cs" />
     <Compile Include="MathUtils.cs" />
     <Compile Include="RandomExtensions.cs" />
+    <Compile Include="Particles\ParticleLink.cs" />
+    <Compile Include="Particles\ParticleConstraint.cs" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ItemGroup>

File CycloNet.Physics/Particles/ParticleConstraint.cs

+using System;
+using System.Collections.Generic;
+using OpenTK;
+
+namespace CycloNet.Physics.Particles
+{
+    public abstract class ParticleConstraint : IParticleContactGenerator
+    {
+        /// <summary>
+        /// Particle connected to this constraint.
+        /// </summary>
+        public Particle Particle;
+
+        /// <summary>
+        /// The point to which the particle is anchored.
+        /// </summary>
+        public Vector3 Anchor;
+
+        protected ParticleConstraint()
+        {
+        }
+
+        protected ParticleConstraint(Particle particle, Vector3 anchor)
+        {
+            this.Particle = particle;
+            this.Anchor = anchor;
+        }
+
+        protected float CurrentLength
+        {
+            get { return (Particle.Position - Anchor).Length; }
+        }
+
+        public abstract void AddContacts(List<ParticleContact> contacts, int maxContacts);
+    }
+
+    public class ParticleCableConstraint : ParticleConstraint
+    {
+        public float MaxLength;
+        public float Restitution;
+
+        public ParticleCableConstraint()
+        {
+        }
+        
+        public ParticleCableConstraint(Particle particle, Vector3 anchor):
+            base(particle, anchor)
+        {
+        }
+
+        public override void AddContacts(List<ParticleContact> contacts, int maxContacts)
+        {
+            float length = CurrentLength;
+            if (length < MaxLength)
+                return;
+
+            var normal = Anchor - Particle.Position;
+            normal.Normalize();
+
+            contacts.Add(new ParticleContact
+            {
+                Particle0 = Particle,
+                Particle1 = null,
+                ContactNormal = normal,
+                Penetration = length - MaxLength,
+                Restitution = Restitution
+            });
+        }
+    }
+
+    public class ParticleRodConstraint : ParticleConstraint
+    {
+        public float Length;
+
+        public override void AddContacts(List<ParticleContact> contacts, int maxContacts)
+        {
+            // Find the length of the rod
+            float currentLen = CurrentLength;
+
+            // Check if we're over-extended
+            if (currentLen == Length)
+                return;
+
+            var contact = new ParticleContact
+            {
+                Particle0 = Particle,
+                Particle1 = null,
+                Restitution = 0 // no bounciness
+            };
+
+            // Calculate the normal
+            var normal = Anchor - Particle.Position;
+            normal.Normalize();
+
+            // The contact normal depends on whether we're extending or compressing
+            if (currentLen > Length) {
+                contact.ContactNormal = normal;
+                contact.Penetration = currentLen - Length;
+            } else {
+                contact.ContactNormal = -normal;
+                contact.Penetration = Length - currentLen;
+            }
+
+            contacts.Add(contact);
+        }
+    }
+}

File CycloNet.Physics/Particles/ParticleLink.cs

+using System;
+using System.Collections.Generic;
+
+namespace CycloNet.Physics.Particles
+{
+    /// <summary>
+    /// Links connect two particles together, generating a contact if
+    /// they violate the constraints of their link. It is used as a
+    /// base class for cables and rods, and could be used as a base
+    /// class for springs with a limit to their extension..
+    /// </summary>
+    public abstract class ParticleLink : IParticleContactGenerator
+    {
+        public Particle Particle0;
+        public Particle Particle1;
+
+        protected ParticleLink()
+        {
+        }
+
+        protected ParticleLink(Particle particle0, Particle particle1)
+        {
+            Particle0 = particle0;
+            Particle1 = particle1;
+        }
+
+        protected float CurrentLength
+        {
+            get { return (Particle0.Position - Particle1.Position).Length; }
+        }
+
+        public abstract void AddContacts(List<ParticleContact> contacts, int maxContacts);
+    }
+
+    public class ParticleCable : ParticleLink
+    {
+        public float MaxLength;
+        public float Restitution;
+
+        public ParticleCable()
+        {
+        }
+
+        public ParticleCable(Particle particle0, Particle particle1):
+            base(particle0, particle1)
+        {
+        }
+
+        public override void AddContacts(List<ParticleContact> contacts, int maxContacts)
+        {
+            // Find the length of the cable
+            var length = CurrentLength;
+
+            // Check if we're over-extended
+            if (length < MaxLength)
+                return;
+
+            // Calculate the normal
+            var normal = Particle1.Position - Particle0.Position;
+            normal.Normalize();
+
+            // Otherwise return the contact
+            contacts.Add(new ParticleContact
+            {
+                Particle0 = Particle0,
+                Particle1 = Particle1,
+                ContactNormal = normal,
+                Penetration = length - MaxLength,
+                Restitution = Restitution
+            });
+        }
+    }
+
+    public class ParticleRod : ParticleLink
+    {
+        public float Length;
+
+        public ParticleRod()
+        {
+        }
+
+        public ParticleRod(Particle particle0, Particle particle1):
+            base(particle0, particle1)
+        {
+        }
+
+        public override void AddContacts(List<ParticleContact> contacts, int maxContacts)
+        {
+            // Find the length of the rod
+            float currentLen = CurrentLength;
+
+            // Check if we're over-extended
+            if (currentLen == Length)
+                return;
+
+            var contact = new ParticleContact
+            {
+                Particle0 = Particle0,
+                Particle1 = Particle1,
+                Restitution = 0 // no bounciness
+            };
+
+            // Calculate the normal
+            var normal = Particle1.Position - Particle0.Position;
+            normal.Normalize();
+
+            // The contact normal depends on whether we're extending or compressing
+            if (currentLen > Length) {
+                contact.ContactNormal = normal;
+                contact.Penetration = currentLen - Length;
+            } else {
+                contact.ContactNormal = -normal;
+                contact.Penetration = Length - currentLen;
+            }
+
+            contacts.Add(contact);
+        }
+    }
+}
+