示例#1
0
    /*!
     * Calculate all collisions in the \ref World, according to \p flags and
     * place the results in \p worldCollisions.
     *
     * If \p getCollisionsWith is non-NULL, only collision pairs that \p
     * getCollisionsWith is part of are considered, instead of all collisions
     * in the \p World. This can be useful to test whether \p getCollisionsWith
     * collides with some proxy when other colliding proxies do not matter.
     *
     * \param getCollisionsWith If NULL this method returns all collisions of all
     * proxies. If non-NULL, only collisions with \p getCollisionsWith are
     * returned, all proxies not colliding with this one are ignored. The proxy
     * must be a toplevel proxy, child-proxies (i.e. a \ref Proxy that is a
     * child of another \ref Proxy) are not supported.
     */
    void Pipeline::calculateAllCollisions(unsigned int flags, WorldCollisions* worldCollisions, Proxy* getCollisionsWith) {
        mSkipMiddlePhase = false;
        mSkipNarrowPhase = false;
        mCollisionInfos.clear();
        if (getUsePipelining() && mWorkerPool->getDisableThreads()) {
            setUsePipelining(false);
        }
        if (!mBroadPhaseJobs->empty() || !mMiddlePhaseJobs->empty() || !mNarrowPhaseJobs->empty()) {
            throw Exception("Pipeline: internal error: not all job lists empty at beginning of collision detection");
        }

        if (flags & World::COLLISIONFLAG_SKIP_MIDDLE_PHASE) {
            mSkipMiddlePhase = true;

            // skip middle phase => skip narrow phase
            flags |= World::COLLISIONFLAG_SKIP_NARROW_PHASE;
        }
        if (flags & World::COLLISIONFLAG_SKIP_NARROW_PHASE) {
            mSkipNarrowPhase = true;
        }

        // AB: when pipelined, we use a single list and start jobs whenever they
        //     are created.
        //     when not pipelined, we have to differ between new middle and
        //     narrow phase jobs and therefore use different lists.
        if (getUsePipelining()) {
            mCurrentPhase = PHASE_NONE;
            mBroadPhaseJobs = &mPipelinedJobs;
            mMiddlePhaseJobs = &mPipelinedJobs;
            mNarrowPhaseJobs = &mPipelinedJobs;
        } else {
            mCurrentPhase = PHASE_BROADPHASE;
            mBroadPhaseJobs = &mUnpipelinedBroadPhaseJobs;
            mMiddlePhaseJobs = &mUnpipelinedMiddlePhaseJobs;
            mNarrowPhaseJobs = &mUnpipelinedNarrowPhaseJobs;
        }

        mMinCollisionsPerBroadPhaseJob = UINT_MAX;
        mMaxCollisionsPerBroadPhaseJob = 0;
        mTotalBroadPhaseCollisions = 0;

        Timing time;
        mWorld->getBroadPhase()->createBroadPhaseJobs(getCollisionsWith);
        if (!mWorld->getBroadPhase()->getJobCollection()) {
            throw NullPointerException("mWorld->getBroadPhase()->getJobCollection()");
        }

        PipelineThreadJobCollection* broadPhaseJobCollection = mWorld->getBroadPhase()->getJobCollection();
        if (broadPhaseJobCollection->getJobsInCollectionCount() == 0) {
            // AB: we essentially skip the broadphase here.
            // this is
            // a) a little hack to enforce the usage of the job collection
            //    (instead of plain ThreadJobs) in the broadphase
            //    -> some algorithms (e.g. the spatialhash) require this, to
            //       get notified about completion of the broadphase
            // b) a small optimization for the case that the broadphase
            //    already knows that nothing will ever collide and thus
            //    won't need to create jobs at all
            //    (e.g. no collidable proxies in the world)
            // of course b) doesnt really matter :-)
            mSkipMiddlePhase = true;
            mSkipNarrowPhase = true;
        } else {
            broadPhaseJobCollection->start();
            worldCollisions->setBroadPhaseCollisions(mWorld->getBroadPhase()->getBroadPhaseCollisions());
        }

        mWorld->getBroadPhase()->getJobCollection()->addStartOnceCompleted(mBroadPhasePostProcessingJobCollection);
        mBroadPhasePostProcessingJobCollection->setSkipMiddlePhase(mSkipMiddlePhase);

        // mBroadPhasePostProcessingJobCollection is just a dummy job collection
        // without any actual jobs
        // -> we just need the allJobsCompleted() method
        mBroadPhasePostProcessingJobCollection->setAllJobsAreAdded();

        if (!mSkipMiddlePhase) {
            mMiddlePhaseJobCreator->createSelfCollisionJobs(mWorld->getSelfcollisionProxies());

            if (mDetectorDeformManager) {
                // note: once this is called, internal job collections of the
                // DetectorDeformAlgorithm objects will add their jobs to the
                // pipeline - but the _pipeline_ will actually start the jobs
                mDetectorDeformManager->notifyPipelineStarted();
            }

            // add deformable self-collision jobs
            if (mDetectorDeformManager) {
                const std::list<Proxy*>& selfCollisionProxies = mWorld->getSelfcollisionProxies();
                for (std::list<Proxy*>::const_iterator it = selfCollisionProxies.begin(); it != selfCollisionProxies.end(); ++it) {
                    if (!((*it)->getProxyType() & PROXYTYPE_DEFORMABLE)) {
                        continue;
                    }

                    if (mWorld->getUseCollisionCaching()) {
                        bool cacheApplied = mWorld->getCollisionCache()->applyCacheIfAvailable(*it, *it);
                        if (cacheApplied) {
                            continue;
                        }
                    }

                    DetectorDeformAlgorithm* algorithm = mDetectorDeformManager->pickAlgorithmFor(*it, *it);
                    if (algorithm) {
                        CollisionPair pair;

                        // FIXME: make CollisionPair store Proxy pointers, not
                        // BoundingVolume pointers.
                        pair.bvol1 = (*it)->getBvHierarchyNode()->getBoundingVolume();
                        pair.bvol2 = pair.bvol1;
                        algorithm->createCollisionJobFor(pair);
                    }
                }
            }
        }

        if (!getUsePipelining()) {
            completeCurrentPhase();
            time.stop();
            mWorld->getCurrentDebugLogEntry()->addTiming("BroadPhase", time);

            time.restart();
            mCurrentPhase = PHASE_MIDDLEPHASE;
            completeCurrentPhase();
            time.stop();
            mWorld->getCurrentDebugLogEntry()->addTiming("MiddlePhase", time);

            time.restart();
            mCurrentPhase = PHASE_NARROWPHASE;
            completeCurrentPhase();
            time.stop();
            mWorld->getCurrentDebugLogEntry()->addTiming("NarrowPhase", time);
        } else {
            completeCurrentPhase();
            time.stop();

            // in a pipelined run we cannot differ between phases
            // AB: note: "BroadPhase", "MiddlePhase" and "NarrowPhase" timings
            //     can still be retrieved from the log - since we havent added
            //     them, they will be nullified Timing objects.
            mWorld->getCurrentDebugLogEntry()->addTiming("Pipeline", time);
        }

        if (mDetectorDeformManager) {
            // reset the job collections of the algorithms
            // note: at this point all jobs must already be completed!
            mDetectorDeformManager->notifyPipelineCompleted();
        }

        // pipeline has been completed at this point. add results to
        // worldCollisions.
        worldCollisions->setRigidBoundingVolumeCollisions(&mMiddlePhaseRigidResults);
        worldCollisions->addNarrowPhaseCollisions(&mCollisionInfos);

        // post-pipeline algorithms
        time.restart();
        startCompletelyUnthreadedAlgorithms(worldCollisions);
        time.stop();
        mWorld->getCurrentDebugLogEntry()->addTiming("UnthreadedPhase", time);

        mWorld->getCurrentDebugLogEntry()->setUIntVariable("BroadPhase job count", mWorld->getBroadPhase()->getJobCollection()->getJobsInCollectionCount());
        mWorld->getCurrentDebugLogEntry()->setUIntVariable("BroadPhase min collisions per job", mMinCollisionsPerBroadPhaseJob);
        mWorld->getCurrentDebugLogEntry()->setUIntVariable("BroadPhase max collisions per job", mMaxCollisionsPerBroadPhaseJob);
        mWorld->getCurrentDebugLogEntry()->setUIntVariable("BroadPhase collisions", mTotalBroadPhaseCollisions);

        // sanity check
        mWorkerPool->waitForCompletion();
        if (mWorkerPool->hasCompletedJobs()) {
            std::cerr << dc_funcinfo << "ERROR: completed jobs left" << std::endl;
            while (mWorkerPool->hasCompletedJobs()) {
                delete mWorkerPool->retrieveCompletedJob();
            }
        }

        mCurrentPhase = PHASE_INVALID;

        if (!mBroadPhaseJobs->empty() || !mMiddlePhaseJobs->empty() || !mNarrowPhaseJobs->empty()) {
            throw Exception("Pipeline: internal error: not all job lists empty at end of collision detection");
        }
    }