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))); }
/// <summary> /// Tries to extract contact prestep, impulse, and body reference data from the given handle. If it's not a contact constraint, returns false. /// </summary> /// <typeparam name="TExtractor">Type of the extractor used to collect contact data from the solver.</typeparam> /// <param name="constraintHandle">Constraint to try to extract data from.</param> /// <param name="extractor">Extractor used to collect contact data from the solver.</param> /// <returns>True if the constraint was a contact type, false otherwise.</returns> public bool TryExtractSolverContactData <TExtractor>(ConstraintHandle constraintHandle, ref TExtractor extractor) where TExtractor : struct, ISolverContactDataExtractor { ref var constraintLocation = ref Solver.HandleToConstraint[constraintHandle.Value];
/// <summary> /// Allocates a slot in the batch. /// </summary> /// <param name="typeBatch">Type batch to allocate in.</param> /// <param name="handle">Handle of the constraint to allocate. Establishes a link from the allocated constraint to its handle.</param> /// <param name="bodyIndices">Pointer to a list of body indices (not handles!) with count equal to the type batch's expected number of involved bodies.</param> /// <param name="pool">Allocation provider to use if the type batch has to be resized.</param> /// <returns>Index of the slot in the batch.</returns> public unsafe abstract int Allocate(ref TypeBatch typeBatch, ConstraintHandle handle, int *bodyIndices, BufferPool pool);