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); }