Esempio n. 1
0
LoopResult R_ForAllSubspaceLumContacts(ConvexSubspace &subspace, std::function<LoopResult (Lumobj &)> func)
{
    ContactList &list = R_ContactList(subspace, ContactLumobj);
    for(ContactList::Node *node = list.begin(); node; node = node->next)
    {
        if(auto result = func(*static_cast<Lumobj *>(node->obj)))
            return result;
    }
    return LoopContinue;
}
    /**
     * Link the contact in all non-degenerate subspaces which touch the linked
     * object (tests are done with subspace bounding boxes and the spread test).
     *
     * @param contact  Contact to be spread.
     */
    void spreadContact(Contact &contact)
    {
        ConvexSubspace &subspace = contact.objectBspLeafAtOrigin().subspace();

        R_ContactList(subspace, contact.type()).link(&contact);

        // Spread to neighboring BSP leafs.
        subspace.setValidCount(++validCount);

        _spread.contact      = &contact;
        _spread.contactAABox = contact.objectAABox();

        spreadInSubspace(subspace);
    }
    void maybeSpreadOverEdge(HEdge *hedge)
    {
        DENG2_ASSERT(_spread.contact != 0);

        if(!hedge) return;

        ConvexSubspace &subspace = hedge->face().mapElementAs<ConvexSubspace>();
        SectorCluster &cluster   = subspace.cluster();

        // There must be a back BSP leaf to spread to.
        if(!hedge->hasTwin() || !hedge->twin().hasFace() || !hedge->twin().face().hasMapElement())
            return;

        ConvexSubspace &backSubspace = hedge->twin().face().mapElementAs<ConvexSubspace>();
        SectorCluster &backCluster   = backSubspace.cluster();

        // Which way does the spread go?
        if(!(subspace.validCount() == validCount &&
             backSubspace.validCount() != validCount))
        {
            return; // Not eligible for spreading.
        }

        // Is the leaf on the back side outside the origin's AABB?
        AABoxd const &aaBox = backSubspace.poly().aaBox();
        if(aaBox.maxX <= _spread.contactAABox.minX || aaBox.minX >= _spread.contactAABox.maxX ||
           aaBox.maxY <= _spread.contactAABox.minY || aaBox.minY >= _spread.contactAABox.maxY)
            return;

        // Too far from the edge?
        coord_t const length   = (hedge->twin().origin() - hedge->origin()).length();
        coord_t const distance = pointOnHEdgeSide(*hedge, _spread.contact->objectOrigin()) / length;
        if(abs(distance) >= _spread.contact->objectRadius())
            return;

        // Do not spread if the sector on the back side is closed with no height.
        if(!backCluster.hasWorldVolume())
            return;

        if(backCluster.visCeiling().heightSmoothed() <= cluster.visFloor().heightSmoothed() ||
           backCluster.visFloor().heightSmoothed() >= cluster.visCeiling().heightSmoothed())
            return;

        // Are there line side surfaces which should prevent spreading?
        if(hedge->hasMapElement())
        {
            LineSideSegment const &seg = hedge->mapElementAs<LineSideSegment>();

            // On which side of the line are we? (distance is from segment to origin).
            LineSide const &facingLineSide = seg.line().side(seg.lineSide().sideId() ^ (distance < 0));

            // One-way window?
            if(!facingLineSide.back().hasSections())
                return;

            SectorCluster const &fromCluster = facingLineSide.isFront()? cluster : backCluster;
            SectorCluster const &toCluster   = facingLineSide.isFront()? backCluster : cluster;

            // Might a material cover the opening?
            if(facingLineSide.hasSections() && facingLineSide.middle().hasMaterial())
            {
                // Stretched middles always cover the opening.
                if(facingLineSide.isFlagged(SDF_MIDDLE_STRETCH))
                    return;

                // Determine the opening between the visual sector planes at this edge.
                coord_t openBottom;
                if(toCluster.visFloor().heightSmoothed() > fromCluster.visFloor().heightSmoothed())
                {
                    openBottom = toCluster.visFloor().heightSmoothed();
                }
                else
                {
                    openBottom = fromCluster.visFloor().heightSmoothed();
                }

                coord_t openTop;
                if(toCluster.visCeiling().heightSmoothed() < fromCluster.visCeiling().heightSmoothed())
                {
                    openTop = toCluster.visCeiling().heightSmoothed();
                }
                else
                {
                    openTop = fromCluster.visCeiling().heightSmoothed();
                }

                MaterialAnimator &matAnimator = facingLineSide.middle().material().getAnimator(Rend_MapSurfaceMaterialSpec());

                // Ensure we have up to date info about the material.
                matAnimator.prepare();

                if(matAnimator.isOpaque() && matAnimator.dimensions().y >= openTop - openBottom)
                {
                    // Possibly; check the placement.
                    WallEdge edge(WallSpec::fromMapSide(facingLineSide, LineSide::Middle),
                                     *facingLineSide.leftHEdge(), Line::From);

                    if(edge.isValid() && edge.top().z() > edge.bottom().z() &&
                       edge.top().z() >= openTop && edge.bottom().z() <= openBottom)
                        return;
                }
            }
        }

        // During the next step this contact will spread from the back leaf.
        backSubspace.setValidCount(validCount);

        R_ContactList(backSubspace, _spread.contact->type()).link(_spread.contact);

        spreadInSubspace(backSubspace);
    }