public override void Initialize(ContentArchive content, Camera camera) { camera.Position = new Vector3(0, 10, 40); camera.Yaw = 0; camera.Pitch = 0; //Note the higher stiffness on contacts for this demo. That's not ideal for general stability at the demo timestep duration default of 60hz, but //this demo doesn't have any significant solver complexity and we want to see the CCD in action more clearly- which means more rigid contact. //Having objects bounce a bunch on impact makes it harder to see. //Also note that the PositionFirstTimestepper is the simplest timestepping mode, but since it integrates velocity into position at the start of the frame, directly modified velocities outside of the timestep //will be integrated before collision detection or the solver has a chance to intervene. That's fine in this demo. Other built-in options include the PositionLastTimestepper and the SubsteppingTimestepper. //Note that the timestepper also has callbacks that you can use for executing logic between processing stages, like BeforeCollisionDetection. Simulation = Simulation.Create(BufferPool, new DemoNarrowPhaseCallbacks() { ContactSpringiness = new SpringSettings(120, 1) }, new DemoPoseIntegratorCallbacks(new Vector3(0, -10, 0)), new PositionFirstTimestepper()); var shape = new Box(1, 1, 1); shape.ComputeInertia(1, out var inertia); var shapeIndex = Simulation.Shapes.Add(shape); for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { //These two falling dynamics have pretty small speculative margins. The second one uses continuous collision detection sweeps to generate speculative contacts. Simulation.Bodies.Add(BodyDescription.CreateDynamic(new Vector3(-4 - 2 * j, 100 + (i + j) * 2, i * 2), new BodyVelocity { Linear = new Vector3(0, -150, 0) }, inertia, new CollidableDescription(shapeIndex, 0.01f), new BodyActivityDescription(0.01f))); //The minimum progression duration parameter at 1e-3 means the CCD sweep won't miss any collisions that last at least 1e-3 units of time- so, if time is measured in seconds, //then this will capture any collision that an update rate of 1000hz would. //Note also that the sweep convergence threshold is actually pretty loose at 100hz. Despite that, it can still lead to reasonably good speculative contacts with solid impact behavior. //That's because the sweep does not directly generate contacts- it generates a time of impact estimate, and then the discrete contact generation //runs to create the actual contact manifold. That provides high quality contact positions and speculative depths. //If the ground that these boxes were smashing into was something like a mesh- which is infinitely thin- you may want to increase the sweep accuracy. Simulation.Bodies.Add(BodyDescription.CreateDynamic(new Vector3(4 + 2 * j, 100 + (i + j) * 2, i * 2), new BodyVelocity { Linear = new Vector3(0, -150, 0) }, inertia, new CollidableDescription(shapeIndex, 0.01f, ContinuousDetectionSettings.Continuous(1e-3f, 1e-2f)), new BodyActivityDescription(0.01f))); } } rolloverInfo = new RolloverInfo(); rolloverInfo.Add(new Vector3(-12, 2, 0), "Discrete"); rolloverInfo.Add(new Vector3(12, 2, 0), "Continuous"); //Build a couple of spinners to ram into each other to showcase angular CCD. Note that the spin speeds are slightly different- that helps avoid //synchronization that makes the blades frequently miss each other, which sorta ruins a CCD demo. spinnerMotorA = BuildSpinner(new Vector3(-5, 10, -5), 53); spinnerMotorB = BuildSpinner(new Vector3(5, 10, -5), 59); rolloverInfo.Add(new Vector3(0, 12, -5), "High angular velocity continuous detection"); Simulation.Statics.Add(new StaticDescription(new Vector3(0, -5f, 0), new CollidableDescription(Simulation.Shapes.Add(new Box(300, 10, 300)), 0.1f))); }
public unsafe override void Initialize(ContentArchive content, Camera camera) { camera.Position = new Vector3(0, 25, 80); camera.Yaw = 0; camera.Pitch = 0; timestepper = new SubsteppingTimestepper(8); Simulation = Simulation.Create(BufferPool, new DemoNarrowPhaseCallbacks() { ContactSpringiness = new SpringSettings(120, 120) }, new DemoPoseIntegratorCallbacks(new Vector3(0, -10, 0)), timestepper, 8); rolloverInfo = new RolloverInfo(); { //We'll create a 0 level arm rope like the one from the RopeStabilityDemo. No skip constraints, though- and the mass ratio will be 1000:1 instead of 100:1! var startLocation = new Vector3(15, 40, 0); const float bodySpacing = 0.3f; const float bodyRadius = 0.5f; var springSettings = new SpringSettings(240, 480); var bodyHandles = RopeStabilityDemo.BuildRope(Simulation, startLocation, 12, bodyRadius, bodySpacing, 0, 1, 0, springSettings); var bigWreckingBall = new Sphere(5); bigWreckingBall.ComputeInertia(1000, out var bigWreckingBallInertia); RopeStabilityDemo.AttachWreckingBall(Simulation, bodyHandles, bodyRadius, bodySpacing, 0, bigWreckingBall.Radius, bigWreckingBallInertia, Simulation.Shapes.Add(bigWreckingBall), springSettings); rolloverInfo.Add(startLocation + new Vector3(0, 2, 0), "1000:1 mass ratio"); } { //Stack with a heavy block on top. Note that the contact springiness we chose in the DemoNarrowPhaseCallbacks above is important to making the stack resist the weight of the top block. //It's also the reason why we need higher substeps- 120hz frequency is too high for 60hz solving! Watch what happens when you drop the substep count to 3. //(Note that the demos timestep frequency is 60hz, so 4 substeps is a 240hz solve rate- twice the 120hz contact frequency.) var boxShape = new Box(4, 0.5f, 6f); boxShape.ComputeInertia(1, out var boxInertia); //Note that sleeping is disabled with a negative velocity threshold. We want to watch the stack as we change simulation settings; if it's inactive, it won't respond! var boxDescription = BodyDescription.CreateDynamic(new Vector3(), boxInertia, new CollidableDescription(Simulation.Shapes.Add(boxShape), 0.1f), new BodyActivityDescription(-1f)); for (int i = 0; i < 20; ++i) { boxDescription.Pose = new RigidPose(new Vector3(0, 0.5f + boxShape.Height * (i + 0.5f), 0), QuaternionEx.CreateFromAxisAngle(Vector3.UnitY, MathF.PI * 0.05f * i)); Simulation.Bodies.Add(boxDescription); } var topBlockShape = new Box(8, 2, 8); topBlockShape.ComputeInertia(200, out var topBlockInertia); Simulation.Bodies.Add( BodyDescription.CreateDynamic(boxDescription.Pose.Position + new Vector3(0, boxShape.HalfHeight + 1f, 0), topBlockInertia, new CollidableDescription(Simulation.Shapes.Add(topBlockShape), 0.1f), new BodyActivityDescription(-1f))); rolloverInfo.Add(boxDescription.Pose.Position + new Vector3(0, 4, 0), "200:1 mass ratio"); } { //Now a weird rotating multi-arm thing. Long constraint sequences with high leverages under stress are a really tough problem for iterative velocity solvers. //(Fortunately, all 5 degrees of freedom of each hinge constraint are solved analytically, so the convergence issues aren't quite as bad as they could be.) var basePosition = new Vector3(-20, 20, 0); var boxShape = new Box(0.5f, 0.5f, 3f); var boxCollidable = new CollidableDescription(Simulation.Shapes.Add(boxShape), 0.1f); boxShape.ComputeInertia(1, out var boxInertia); var linkDescription = BodyDescription.CreateDynamic(new Vector3(), boxInertia, boxCollidable, new BodyActivityDescription(0.01f)); for (int chainIndex = 0; chainIndex < 4; ++chainIndex) { linkDescription.Pose.Position = basePosition + new Vector3(0, 0, chainIndex * 15); var previousLinkHandle = Simulation.Bodies.Add(BodyDescription.CreateKinematic(linkDescription.Pose.Position, boxCollidable, new BodyActivityDescription(0.01f))); for (int linkIndex = 0; linkIndex < 8; ++linkIndex) { var previousPosition = linkDescription.Pose.Position; var offset = new Vector3(boxShape.Width * 1.05f, 0, boxShape.Length - boxShape.Width); linkDescription.Pose.Position += offset; var linkHandle = Simulation.Bodies.Add(linkDescription); Simulation.Solver.Add(previousLinkHandle, linkHandle, new Hinge { LocalHingeAxisA = Vector3.UnitX, LocalHingeAxisB = Vector3.UnitX, LocalOffsetA = offset * 0.5f, LocalOffsetB = offset * -0.5f, //Once again, the choice of high stiffness makes this potentially unstable without substepping. SpringSettings = new SpringSettings(120, 1) }); Simulation.Solver.Add(previousLinkHandle, linkHandle, new AngularAxisMotor { LocalAxisA = Vector3.UnitX, TargetVelocity = .25f, Settings = new MotorSettings(float.MaxValue, 0.0001f) }); previousLinkHandle = linkHandle; } } rolloverInfo.Add(basePosition + new Vector3(0, 4, 0), "High stiffness, long lever arm motorized chain"); } Simulation.Statics.Add(new StaticDescription(new Vector3(0, 0, 0), new CollidableDescription(Simulation.Shapes.Add(new Box(200, 1, 200)), 0.1f))); }