示例#1
0
        protected Simulation(BufferPool bufferPool, SimulationAllocationSizes initialAllocationSizes)
        {
            BufferPool = bufferPool;
            Shapes     = new Shapes(bufferPool, initialAllocationSizes.ShapesPerType);
            BroadPhase = new BroadPhase(bufferPool, initialAllocationSizes.Bodies, initialAllocationSizes.Bodies + initialAllocationSizes.Statics);
            Bodies     = new Bodies(bufferPool, Shapes, BroadPhase,
                                    initialAllocationSizes.Bodies,
                                    initialAllocationSizes.Islands,
                                    initialAllocationSizes.ConstraintCountPerBodyEstimate);
            Statics = new Statics(bufferPool, Shapes, Bodies, BroadPhase, initialAllocationSizes.Statics);

            Solver = new Solver(Bodies, BufferPool, 8,
                                initialCapacity: initialAllocationSizes.Constraints,
                                initialIslandCapacity: initialAllocationSizes.Islands,
                                minimumCapacityPerTypeBatch: initialAllocationSizes.ConstraintsPerTypeBatch);
            constraintRemover = new ConstraintRemover(BufferPool, Bodies, Solver);
            Sleeper           = new IslandSleeper(Bodies, Solver, BroadPhase, constraintRemover, BufferPool);
            Awakener          = new IslandAwakener(Bodies, Statics, Solver, BroadPhase, Sleeper, bufferPool);
            Statics.awakener  = Awakener;
            Solver.awakener   = Awakener;
            Bodies.Initialize(Solver, Awakener);
            PoseIntegrator            = new PoseIntegrator(Bodies, Shapes, BroadPhase);
            SolverBatchCompressor     = new BatchCompressor(Solver, Bodies);
            BodyLayoutOptimizer       = new BodyLayoutOptimizer(Bodies, BroadPhase, Solver, bufferPool);
            ConstraintLayoutOptimizer = new ConstraintLayoutOptimizer(Bodies, Solver);
        }
示例#2
0
        /// <summary>
        /// Constructs a simulation supporting dynamic movement and constraints with the specified narrow phase callbacks.
        /// </summary>
        /// <param name="bufferPool">Buffer pool used to fill persistent structures and main thread ephemeral resources across the engine.</param>
        /// <param name="narrowPhaseCallbacks">Callbacks to use in the narrow phase.</param>
        /// <param name="poseIntegratorCallbacks">Callbacks to use in the pose integrator.</param>
        /// <param name="timestepper">Timestepper that defines how the simulation state should be updated.</param>
        /// <param name="solverIterationCount">Number of iterations the solver should use.</param>
        /// <param name="solverFallbackBatchThreshold">Number of synchronized batches the solver should maintain before falling back to a lower quality jacobi hybrid solver.</param>
        /// <param name="initialAllocationSizes">Allocation sizes to initialize the simulation with. If left null, default values are chosen.</param>
        /// <returns>New simulation.</returns>
        public static Simulation Create <TNarrowPhaseCallbacks, TPoseIntegratorCallbacks>(
            BufferPool bufferPool, TNarrowPhaseCallbacks narrowPhaseCallbacks, TPoseIntegratorCallbacks poseIntegratorCallbacks, ITimestepper timestepper,
            int solverIterationCount = 8, int solverFallbackBatchThreshold = 64, SimulationAllocationSizes?initialAllocationSizes = null)
            where TNarrowPhaseCallbacks : struct, INarrowPhaseCallbacks
            where TPoseIntegratorCallbacks : struct, IPoseIntegratorCallbacks
        {
            if (initialAllocationSizes == null)
            {
                initialAllocationSizes = new SimulationAllocationSizes
                {
                    Bodies        = 4096,
                    Statics       = 4096,
                    ShapesPerType = 128,
                    ConstraintCountPerBodyEstimate = 8,
                    Constraints             = 16384,
                    ConstraintsPerTypeBatch = 256
                };
            }

            var simulation     = new Simulation(bufferPool, initialAllocationSizes.Value, solverIterationCount, solverFallbackBatchThreshold, timestepper);
            var poseIntegrator = new PoseIntegrator <TPoseIntegratorCallbacks>(simulation.Bodies, simulation.Shapes, simulation.BroadPhase, poseIntegratorCallbacks);

            simulation.PoseIntegrator = poseIntegrator;
            var narrowPhase = new NarrowPhase <TNarrowPhaseCallbacks>(simulation,
                                                                      DefaultTypes.CreateDefaultCollisionTaskRegistry(), DefaultTypes.CreateDefaultSweepTaskRegistry(),
                                                                      narrowPhaseCallbacks, initialAllocationSizes.Value.Islands + 1);

            DefaultTypes.RegisterDefaults(simulation.Solver, narrowPhase);
            simulation.NarrowPhase             = narrowPhase;
            simulation.Sleeper.pairCache       = narrowPhase.PairCache;
            simulation.Awakener.pairCache      = narrowPhase.PairCache;
            simulation.Solver.pairCache        = narrowPhase.PairCache;
            simulation.BroadPhaseOverlapFinder = new CollidableOverlapFinder <TNarrowPhaseCallbacks>(narrowPhase, simulation.BroadPhase);

            //We defer initialization until after all the other simulation bits are constructed.
            poseIntegrator.Callbacks.Initialize(simulation);
            narrowPhase.Callbacks.Initialize(simulation);

            return(simulation);
        }
示例#3
0
        //TODO: I wonder if people will abuse the dt-as-parameter to the point where we should make it a field instead, like it effectively was in v1.
        /// <summary>
        /// Performs one timestep of the given length.
        /// </summary>
        /// <remarks>
        /// Be wary of variable timesteps. They can harm stability. Whenever possible, keep the timestep the same across multiple frames unless you have a specific reason not to.
        /// </remarks>
        /// <param name="dt">Duration of the time step in time.</param>
        public void Timestep(float dt, IThreadDispatcher threadDispatcher = null)
        {
            ProfilerClear();
            ProfilerStart(this);
            //Note that the first behavior-affecting stage is actually the pose integrator. This is a shift from v1, where collision detection went first.
            //This is a tradeoff:
            //1) Any externally set velocities will be integrated without input from the solver. The v1-style external velocity control won't work as well-
            //the user would instead have to change velocities after the pose integrator runs. This isn't perfect either, since the pose integrator is also responsible
            //for updating the bounding boxes used for collision detection.
            //2) By bundling bounding box calculation with pose integration, you avoid redundant pose and velocity memory accesses.
            //3) Generated contact positions are in sync with the integrated poses.
            //That's often helpful for gameplay purposes- you don't have to reinterpret contact data when creating graphical effects or positioning sound sources.

            //TODO: This is something that is possibly worth exposing as one of the generic type parameters. Users could just choose the order arbitrarily.
            //Or, since you're talking about something that happens once per frame instead of once per collision pair, just provide a simple callback.
            //(Or maybe an enum even?)
            //#1 is a difficult problem, though. There is no fully 'correct' place to change velocities. We might just have to bite the bullet and create a
            //inertia tensor/bounding box update separate from pose integration. If the cache gets evicted in between (virtually guaranteed unless no stages run),
            //this basically means an extra 100-200 microseconds per frame on a processor with ~20GBps bandwidth simulating 32768 bodies.

            //Note that the reason why the pose integrator comes first instead of, say, the solver, is that the solver relies on world space inertias calculated by the pose integration.
            //If the pose integrator doesn't run first, we either need
            //1) complicated on demand updates of world inertia when objects are added or local inertias are changed or
            //2) local->world inertia calculation before the solver.

            ProfilerStart(PoseIntegrator);
            PoseIntegrator.Update(dt, BufferPool, threadDispatcher);
            ProfilerEnd(PoseIntegrator);

            ProfilerStart(BroadPhase);
            BroadPhase.Update(threadDispatcher);
            ProfilerEnd(BroadPhase);

            ProfilerStart(BroadPhaseOverlapFinder);
            BroadPhaseOverlapFinder.DispatchOverlaps(threadDispatcher);
            ProfilerEnd(BroadPhaseOverlapFinder);

            ProfilerStart(NarrowPhase);
            NarrowPhase.Flush(threadDispatcher, threadDispatcher != null && Deterministic);
            ProfilerEnd(NarrowPhase);

            ProfilerStart(Solver);
            if (threadDispatcher == null)
            {
                Solver.Update(dt);
            }
            else
            {
                Solver.MultithreadedUpdate(threadDispatcher, BufferPool, dt);
            }
            ProfilerEnd(Solver);

            //Note that constraint optimization should be performed after body optimization, since body optimization moves the bodies- and so affects the optimal constraint position.
            //TODO: The order of these optimizer stages is performance relevant, even though they don't have any effect on correctness.
            //You may want to try them in different locations to see how they impact cache residency.
            ProfilerStart(BodyLayoutOptimizer);
            if (threadDispatcher == null)
            {
                BodyLayoutOptimizer.IncrementalOptimize();
            }
            else
            {
                BodyLayoutOptimizer.IncrementalOptimize(BufferPool, threadDispatcher);
            }
            ProfilerEnd(BodyLayoutOptimizer);

            ProfilerStart(ConstraintLayoutOptimizer);
            ConstraintLayoutOptimizer.Update(BufferPool, threadDispatcher);
            ProfilerEnd(ConstraintLayoutOptimizer);

            ProfilerStart(SolverBatchCompressor);
            SolverBatchCompressor.Compress(BufferPool, threadDispatcher, threadDispatcher != null && Deterministic);
            ProfilerEnd(SolverBatchCompressor);

            ProfilerEnd(this);
        }