/** * @param side Line side for which to determine covered opening status. * * @return @c true iff there is a "middle" material on @a side which * completely covers the open range. * * @todo fixme: Should use the visual plane heights of sector clusters. */ static bool middleMaterialCoversOpening(LineSide const &side) { if(!side.hasSector()) return false; // Never. if(!side.hasSections()) return false; if(!side.middle().hasMaterial()) return false; // Stretched middles always cover the opening. if(side.isFlagged(SDF_MIDDLE_STRETCH)) return true; // Ensure we have up to date info about the material. MaterialSnapshot const &ms = side.middle().material().prepare(Rend_MapSurfaceMaterialSpec()); if(ms.isOpaque() && !side.middle().blendMode() && side.middle().opacity() >= 1) { if(side.leftHEdge()) // possibility of degenerate BSP leaf { coord_t openRange, openBottom, openTop; openRange = visOpenRange(side, &openBottom, &openTop); if(ms.height() >= openRange) { // Possibly; check the placement. WallEdge edge(WallSpec::fromMapSide(side, LineSide::Middle), *side.leftHEdge(), Line::From); return (edge.isValid() && edge.top().z() > edge.bottom().z() && edge.top().z() >= openTop && edge.bottom().z() <= openBottom); } } } return false; }
/// @todo fixme: Should use the visual plane heights of sector clusters. static bool middleMaterialCoversOpening(LineSide const &side) { if(!side.hasSector()) return false; // Never. if(!side.hasSections()) return false; if(!side.middle().hasMaterial()) return false; MaterialAnimator &matAnimator = side.middle().material().getAnimator(Rend_MapSurfaceMaterialSpec()); // Ensure we have up to date info about the material. matAnimator.prepare(); // Might the material cover the opening? if(matAnimator.isOpaque() && !side.middle().blendMode() && side.middle().opacity() >= 1) { // Stretched middles always cover the opening. if(side.isFlagged(SDF_MIDDLE_STRETCH)) return true; Sector const &frontSec = side.sector(); Sector const *backSec = side.back().sectorPtr(); // Determine the opening between the visual sector planes at this edge. coord_t openBottom; if(backSec && backSec->floor().heightSmoothed() > frontSec.floor().heightSmoothed()) { openBottom = backSec->floor().heightSmoothed(); } else { openBottom = frontSec.floor().heightSmoothed(); } coord_t openTop; if(backSec && backSec->ceiling().heightSmoothed() < frontSec.ceiling().heightSmoothed()) { openTop = backSec->ceiling().heightSmoothed(); } else { openTop = frontSec.ceiling().heightSmoothed(); } if(matAnimator.dimensions().y >= openTop - openBottom) { // Possibly; check the placement. if(side.leftHEdge()) // possibility of degenerate BSP leaf { WallEdge edge(WallSpec::fromMapSide(side, LineSide::Middle), *side.leftHEdge(), Line::From); return (edge.isValid() && edge.top().z() > edge.bottom().z() && edge.top().z() >= openTop && edge.bottom().z() <= openBottom); } } } return false; }
/// @todo fixme: Should use the visual plane heights of sector clusters. bool R_SideBackClosed(LineSide const &side, bool ignoreOpacity) { if(!side.hasSections()) return false; if(!side.hasSector()) return false; if(side.line().isSelfReferencing()) return false; // Never. if(side.considerOneSided()) return true; Sector const &frontSec = side.sector(); Sector const &backSec = side.back().sector(); if(backSec.floor().heightSmoothed() >= backSec.ceiling().heightSmoothed()) return true; if(backSec.ceiling().heightSmoothed() <= frontSec.floor().heightSmoothed()) return true; if(backSec.floor().heightSmoothed() >= frontSec.ceiling().heightSmoothed()) return true; // Perhaps a middle material completely covers the opening? if(side.middle().hasMaterial()) { MaterialAnimator &matAnimator = side.middle().material().getAnimator(Rend_MapSurfaceMaterialSpec()); // Ensure we have up to date info about the material. matAnimator.prepare(); if(ignoreOpacity || (matAnimator.isOpaque() && !side.middle().blendMode() && side.middle().opacity() >= 1)) { // Stretched middles always cover the opening. if(side.isFlagged(SDF_MIDDLE_STRETCH)) return true; if(side.leftHEdge()) // possibility of degenerate BSP leaf { coord_t openRange, openBottom, openTop; openRange = visOpenRange(side, &openBottom, &openTop); if(matAnimator.dimensions().y >= openRange) { // Possibly; check the placement. WallEdge edge(WallSpec::fromMapSide(side, LineSide::Middle), *side.leftHEdge(), Line::From); return (edge.isValid() && edge.top().z() > edge.bottom().z() && edge.top().z() >= openTop && edge.bottom().z() <= openBottom); } } } } return false; }
/** * Find the neighbor surface for the edge which we will use to calculate the * "blend" properties (e.g., smoothed edge normal). * * @todo: Use the half-edge rings instead of LineOwners. */ Surface *findBlendNeighbor(binangle_t &diff) { diff = 0; // Are we not blending? if(spec.flags.testFlag(WallSpec::NoEdgeNormalSmoothing)) return nullptr; LineSide const &lineSide = lineSideSegment().lineSide(); // Polyobj lines have no owner rings. if(lineSide.line().definesPolyobj()) return nullptr; ClockDirection const direction = (edge? Anticlockwise : Clockwise); LineOwner const &farVertOwner = *lineSide.line().vertexOwner(lineSide.sideId() ^ edge); Line *neighbor; if(R_SideBackClosed(lineSide)) { neighbor = R_FindSolidLineNeighbor(lineSide.line(), farVertOwner, direction, lineSide.sectorPtr(), &diff); } else { neighbor = R_FindLineNeighbor(lineSide.line(), farVertOwner, direction, lineSide.sectorPtr(), &diff); } // No suitable line neighbor? if(!neighbor) return nullptr; // Choose the correct side of the neighbor (determined by which vertex is shared). LineSide *otherSide; if(&neighbor->vertex(edge ^ 1) == &lineSide.vertex(edge)) otherSide = &neighbor->front(); else otherSide = &neighbor->back(); // We can only blend if the neighbor has a surface. if(!otherSide->hasSections()) return nullptr; /// @todo Do not assume the neighbor is the middle section of @var otherSide. return &otherSide->middle(); }