public void ApplyImpulse_DynamicToFrozen(LsmBody applyBody, Particle applyParticle, LsmBody otherBody, double timeCoefficientPrediction, ref List<CollisionSubframeBuffer> collisionBuffer) { Debug.Assert(!applyBody.Equals(otherBody)); // don't allow self-collision here Debug.Assert(!applyBody.Frozen && otherBody.Frozen); foreach (Particle bodyt in otherBody.particles) { if (bodyt.xPos != null) { CheckParticleEdge_D2F( ref applyParticle.ccdDebugInfos, applyParticle, bodyt, bodyt.xPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(bodyt, bodyt.xPos), bodyt.v, bodyt.xPos.v, 0.0) ); } if (bodyt.yPos != null) { CheckParticleEdge_D2F( ref applyParticle.ccdDebugInfos, applyParticle, bodyt, bodyt.yPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(bodyt, bodyt.yPos), bodyt.v, bodyt.yPos.v, 0.0) ); } } }
public void ApplyImpulse( LsmBody applyBody, Particle applyParticle, double timeCoefficientPrediction, ref List <CollisionSubframeBuffer> collisionBuffer // HACK // TODO: remove ref List<> ) { float left = this.left + border, right = this.right - border, bottom = this.bottom + border, top = this.top - border; if (left < 0.0 || right < 0.0 || bottom < 0.0 || top < 0.0) // to prevent computation for incorrect case { return; } Vector2 pos = applyParticle.x; Vector2 velocity = applyParticle.v; Vector2 posNext = pos + velocity * timeCoefficientPrediction; if (posNext.X < left) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(-velocity.X * BodyRepulse.coefficientElasticity, velocity.Y), null, Vector2.ZERO, Vector2.ZERO, (left - pos.X) / velocity.X)); } if (posNext.X > right) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(-velocity.X * BodyRepulse.coefficientElasticity, velocity.Y), null, Vector2.ZERO, Vector2.ZERO, (right - pos.X) / velocity.X)); } if (posNext.Y < bottom) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(velocity.X, -velocity.Y * BodyRepulse.coefficientElasticity), null, Vector2.ZERO, Vector2.ZERO, (bottom - pos.Y) / velocity.Y)); } if (posNext.Y > top) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(velocity.X, -velocity.Y * BodyRepulse.coefficientElasticity), null, Vector2.ZERO, Vector2.ZERO, (top - pos.Y) / velocity.Y)); } }
public void ApplyImpulse( LsmBody applyBody, Particle applyParticle, LsmBody otherBody, // HACK // TODO: try to don't use such information for collisions or formilize this ussage double timeCoefficientPrediction, ref List<CollisionSubframeBuffer> collisionBuffer // HACK // TODO: remove ref List<> ) { Debug.Assert(!applyBody.Equals(otherBody)); // don't allow self-collision here Debug.Assert(!applyBody.Frozen && !otherBody.Frozen); // iterate all possible edges of body and test them with current subframed point foreach (Particle bodyt in otherBody.particles) { if (bodyt.xPos != null) { CheckParticleEdge_D2D( ref applyParticle.ccdDebugInfos, applyParticle, bodyt, bodyt.xPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(bodyt, bodyt.xPos), bodyt.v, bodyt.xPos.v, 0.0) ); } if (bodyt.yPos != null) { CheckParticleEdge_D2D( ref applyParticle.ccdDebugInfos, applyParticle, bodyt, bodyt.yPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(bodyt, bodyt.yPos), bodyt.v, bodyt.yPos.v, 0.0) ); } } }
public void ApplyImpulse( LsmBody applyBody, Particle applyParticle, LsmBody otherBody, // HACK // TODO: try to don't use such information for collisions or formilize this ussage double timeCoefficientPrediction, ref List <CollisionSubframeBuffer> collisionBuffer // HACK // TODO: remove ref List<> ) { Debug.Assert(!applyBody.Equals(otherBody)); // don't allow self-collision here Debug.Assert(!applyBody.Frozen && !otherBody.Frozen); // iterate all possible edges of body and test them with current subframed point foreach (Particle bodyt in otherBody.particles) { if (bodyt.xPos != null) { CheckParticleEdge_D2D( ref applyParticle.ccdDebugInfos, applyParticle, bodyt, bodyt.xPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(bodyt, bodyt.xPos), bodyt.v, bodyt.xPos.v, 0.0) ); } if (bodyt.yPos != null) { CheckParticleEdge_D2D( ref applyParticle.ccdDebugInfos, applyParticle, bodyt, bodyt.yPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(bodyt, bodyt.yPos), bodyt.v, bodyt.yPos.v, 0.0) ); } } }
public void ApplyImpulse_FrozenToDynamic(LsmBody otherBody, Particle otherParticle, LsmBody applyBody, double timeCoefficientPrediction, ref List <CollisionSubframeBuffer> collisionBuffer) { Debug.Assert(!applyBody.Equals(otherBody)); // don't allow self-collision here Debug.Assert(applyBody.Frozen && !otherBody.Frozen); foreach (Particle applyParticle in applyBody.particles) { if (otherParticle.xPos != null) { CheckParticleEdge_F2D( ref applyParticle.ccdDebugInfos, applyParticle, otherParticle, otherParticle.xPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(otherParticle, otherParticle.xPos), otherParticle.v, otherParticle.xPos.v, 0.0) ); } if (otherParticle.yPos != null) { CheckParticleEdge_F2D( ref applyParticle.ccdDebugInfos, applyParticle, otherParticle, otherParticle.yPos, ref collisionBuffer, timeCoefficientPrediction, new CollisionSubframeBuffer(applyParticle, applyParticle.v, new Edge(otherParticle, otherParticle.yPos), otherParticle.v, otherParticle.yPos.v, 0.0) ); } } }
public void ApplyImpulse( LsmBody applyBody, Particle applyParticle, double timeCoefficientPrediction, ref List<CollisionSubframeBuffer> collisionBuffer // HACK // TODO: remove ref List<> ) { float left = this.left + border, right = this.right - border, bottom = this.bottom + border, top = this.top - border; if (left < 0.0 || right < 0.0 || bottom < 0.0 || top < 0.0) // to prevent computation for incorrect case return; Vector2 pos = applyParticle.x; Vector2 velocity = applyParticle.v; Vector2 posNext = pos + velocity * timeCoefficientPrediction; if (posNext.X < left) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(-velocity.X * BodyRepulse.coefficientElasticity, velocity.Y), null, Vector2.ZERO, Vector2.ZERO, (left - pos.X) / velocity.X)); } if (posNext.X > right) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(-velocity.X * BodyRepulse.coefficientElasticity, velocity.Y), null, Vector2.ZERO, Vector2.ZERO, (right - pos.X) / velocity.X)); } if (posNext.Y < bottom) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(velocity.X, -velocity.Y * BodyRepulse.coefficientElasticity), null, Vector2.ZERO, Vector2.ZERO, (bottom - pos.Y) / velocity.Y)); } if (posNext.Y > top) { collisionBuffer.Add(new CollisionSubframeBuffer(applyParticle, new Vector2(velocity.X, -velocity.Y * BodyRepulse.coefficientElasticity), null, Vector2.ZERO, Vector2.ZERO, (top - pos.Y) / velocity.Y)); } }
public static void MakeDataBindingForBody(TestbedWindow window, LsmBody body, int bodyIndex) { // TODO: make this method suitable for N bodies - not only for 2 of them if (window != null && (bodyIndex == 0 || bodyIndex == 1)) { MakeCheckBoxDataBinding(window, "model0" + (bodyIndex + 1) + "DisableCollisions", "Checked", body, "UseWallForce"); MakeCheckBoxDataBinding(window, "model0" + (bodyIndex + 1) + "Freeze", "Checked", body, "Frozen"); } }
private static void RegenerateBody(int bodyIndex, bool[,] blueprint) { if (bodyIndex < 0 || bodyIndex >= world.bodies.Count) { PostMessage(Color.Red, "Invalud world.body index: " + bodyIndex + ". Failed to regenerate body."); return; } LsmBody body = GenerateBody(bodyIndex, blueprint); world.bodies[bodyIndex] = body; }
public static void GenerateBodies(Array blueprints) { int verticalIndex = 0; foreach (bool[,] blueprint in blueprints) { LsmBody body = GenerateBody(verticalIndex, blueprint); world.bodies.Add(body); ++verticalIndex; } }
private static LsmBody GenerateBody(int verticalIndex, bool[,] blueprint) { LsmBody body = new LsmBody( new Vector2(50, 50 + 150 * verticalIndex), new Point(LsmBody.blueprintSpacingX, LsmBody.blueprintSpacingY), new Color3(verticalIndex == 1 ? 1 : 0, verticalIndex == 0 ? 1 : 0, verticalIndex == 2 ? 1 : 0) ); body.GenerateFromBlueprint(blueprint); if (verticalIndex < 2) { MakeDataBindingForBody(Program.testbedWindow, body, verticalIndex); } return(body); }
public static void CollideBodies(LsmBody movingBody, LsmBody blockingBody, double timeCoefficientPrediction, ref List <CollisionSubframeBuffer> collisionBuffer) { foreach (Particle particleOfMovingBody in movingBody.particles) { // impulse & offset collision method particleOfMovingBody.ccdDebugInfos.Clear(); } // Collide with other body if (!movingBody.Frozen && !blockingBody.Frozen) { foreach (Particle particleOfMovingBody in movingBody.particles) { Testbed.world.bodyBodyRepulse.ApplyImpulse( movingBody, particleOfMovingBody, blockingBody, timeCoefficientPrediction, ref collisionBuffer ); } } else if (!movingBody.Frozen && blockingBody.Frozen) { foreach (Particle particleOfMovingBody in movingBody.particles) { Testbed.world.bodyBodyRepulse.ApplyImpulse_DynamicToFrozen( movingBody, particleOfMovingBody, blockingBody, timeCoefficientPrediction, ref collisionBuffer ); } } else if (movingBody.Frozen && !blockingBody.Frozen) { foreach (Particle particleOfBlockingBody in blockingBody.particles) { Testbed.world.bodyBodyRepulse.ApplyImpulse_FrozenToDynamic( blockingBody, particleOfBlockingBody, movingBody, timeCoefficientPrediction, ref collisionBuffer ); } } }
public static void CollideBodies(LsmBody movingBody, LsmBody blockingBody, double timeCoefficientPrediction, ref List<CollisionSubframeBuffer> collisionBuffer) { foreach (Particle particleOfMovingBody in movingBody.particles) { // impulse & offset collision method particleOfMovingBody.ccdDebugInfos.Clear(); } // Collide with other body if (!movingBody.Frozen && !blockingBody.Frozen) { foreach (Particle particleOfMovingBody in movingBody.particles) Testbed.world.bodyBodyRepulse.ApplyImpulse( movingBody, particleOfMovingBody, blockingBody, timeCoefficientPrediction, ref collisionBuffer ); } else if (!movingBody.Frozen && blockingBody.Frozen) { foreach (Particle particleOfMovingBody in movingBody.particles) Testbed.world.bodyBodyRepulse.ApplyImpulse_DynamicToFrozen( movingBody, particleOfMovingBody, blockingBody, timeCoefficientPrediction, ref collisionBuffer ); } else if (movingBody.Frozen && !blockingBody.Frozen) { foreach (Particle particleOfBlockingBody in blockingBody.particles) Testbed.world.bodyBodyRepulse.ApplyImpulse_FrozenToDynamic( blockingBody, particleOfBlockingBody, movingBody, timeCoefficientPrediction, ref collisionBuffer ); } }
static void MakeCheckBoxDataBinding(TestbedWindow window, string checkBoxName, string checkBoxPropertyName, LsmBody body, string bodyPropertyName) { System.Windows.Forms.Control[] controls = window.Controls.Find(checkBoxName, true); if (controls.Length > 0 && controls.GetValue(0) != null && controls.GetValue(0) is System.Windows.Forms.CheckBox) { System.Windows.Forms.CheckBox checkBox = controls.GetValue(0) as System.Windows.Forms.CheckBox; checkBox.DataBindings.Clear(); checkBox.DataBindings.Add(checkBoxPropertyName, body, bodyPropertyName).DataSourceUpdateMode = System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged; } }
private static LsmBody GenerateBody(int verticalIndex, bool[,] blueprint) { LsmBody body = new LsmBody( new Vector2(50, 50 + 150 * verticalIndex), new Point(LsmBody.blueprintSpacingX, LsmBody.blueprintSpacingY), new Color3(verticalIndex == 1 ? 1 : 0, verticalIndex == 0 ? 1 : 0, verticalIndex == 2 ? 1 : 0) ); body.GenerateFromBlueprint(blueprint); if (verticalIndex < 2) { MakeDataBindingForBody(Program.testbedWindow, body, verticalIndex); } return body; }
public void Update() { // update internal processes in bodies, find velocities foreach (LsmBody b in bodies) { if (!b.Frozen) { foreach (IEnvironmentForce force in environmentForces) { if (force is WallForce && !b.UseWallForce) // HACK to apply custom forces // TODO: make correct system { continue; } force.ApplyForce(b.particles); } b.Smooth(); b.DoFracture(); b.UpdateParticlesVelocity(); } else { b.UpdateFrozenParticlesVelocity(); } } // iterate subframes for collision and integration system of bodies List <CollisionSubframeBuffer> collisionBuffer = new List <CollisionSubframeBuffer>(); int iterationsCounter = 0; // to prevent deadlocks const int maxIterations = 64; // to prevent deadlocks double timeCoefficientPrediction = 1.0; bool collisionFound = false; do { for (int i = 0; i < bodies.Count; ++i) { LsmBody bLeft = bodies[i]; if (!bLeft.Frozen && !bLeft.UseWallForce) { bLeft.CollideWithWall(timeCoefficientPrediction, ref collisionBuffer); // TODO: refactor to remove 'ref collisionBuffer' } for (int j = i + 1; j < bodies.Count; ++j) // TODO: implement self-collisions for j == i. { LsmBody bRight = bodies[j]; LsmBody.CollideBodies(bLeft, bRight, timeCoefficientPrediction, ref collisionBuffer); // TODO: refactor to remove 'ref collisionBuffer' LsmBody.CollideBodies(bRight, bLeft, timeCoefficientPrediction, ref collisionBuffer); // TODO: refactor to remove 'ref collisionBuffer' } } double timeCoefficientIntegrate = 0.0; if (collisionBuffer.Count > 0) { CollisionSubframeBuffer subframe = LsmBody.GetEarliestSubframe(collisionBuffer); // WARNING: now we assume that in 1 time moment we have maximum 1 collision in subframe. // TODO: make system ready to handle multi-collision subframes. For example, we'll need to accumulate velocity deltas for every particle and then make summation - not just direct assign of values. timeCoefficientIntegrate = subframe.timeCoefficient; timeCoefficientPrediction -= subframe.timeCoefficient; subframe.particle.v = subframe.vParticle; if (subframe.edge != null) { subframe.edge.start.v = subframe.vEdgeStart; subframe.edge.end.v = subframe.vEdgeEnd; } collisionBuffer.Clear(); collisionFound = true; } else { timeCoefficientIntegrate = timeCoefficientPrediction; collisionFound = false; } if (timeCoefficientIntegrate > 0.0) { foreach (LsmBody b in bodies) { if (!b.Frozen) { b.UpdateParticlesPosition(timeCoefficientIntegrate); } else { b.UpdateFrozenParticlesPosition(); } } } else { Testbed.PostMessage(System.Drawing.Color.Yellow, "Timestep <= 0.0 while subframes iterating!"); // DEBUG } if (++iterationsCounter >= maxIterations) // to prevent deadlocks { Testbed.PostMessage(System.Drawing.Color.Red, "Deadlock detected in HandleCollisions!"); // DEBUG if (LsmBody.pauseOnDeadlock) { Testbed.Paused = true; } break; } }while (collisionFound); }