/** * @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(); }
void Cl_ReadSideDelta(dint /*deltaType*/) { /// @todo Do not assume the CURRENT map. world::Map &map = App_World().map(); dint const index = Reader_ReadUInt16(msgReader); dint const df = Reader_ReadPackedUInt32(msgReader); // Flags. LineSide *side = map.sidePtr(index); DENG2_ASSERT(side != 0); if (df & SIDF_TOP_MATERIAL) { dint matIndex = Reader_ReadPackedUInt16(msgReader); side->top().setMaterial(Cl_LocalMaterial(matIndex)); } if (df & SIDF_MID_MATERIAL) { dint matIndex = Reader_ReadPackedUInt16(msgReader); side->middle().setMaterial(Cl_LocalMaterial(matIndex)); } if (df & SIDF_BOTTOM_MATERIAL) { dint matIndex = Reader_ReadPackedUInt16(msgReader); side->bottom().setMaterial(Cl_LocalMaterial(matIndex)); } if (df & SIDF_LINE_FLAGS) { // The delta includes the entire lowest byte. dint lineFlags = Reader_ReadByte(msgReader); Line &line = side->line(); line.setFlags((line.flags() & ~0xff) | lineFlags, de::ReplaceFlags); } if (df & (SIDF_TOP_COLOR_RED | SIDF_TOP_COLOR_GREEN | SIDF_TOP_COLOR_BLUE)) { Vector3f newColor = side->top().color(); if (df & SIDF_TOP_COLOR_RED) newColor.x = Reader_ReadByte(msgReader) / 255.f; if (df & SIDF_TOP_COLOR_GREEN) newColor.y = Reader_ReadByte(msgReader) / 255.f; if (df & SIDF_TOP_COLOR_BLUE) newColor.z = Reader_ReadByte(msgReader) / 255.f; side->top().setColor(newColor); } if (df & (SIDF_MID_COLOR_RED | SIDF_MID_COLOR_GREEN | SIDF_MID_COLOR_BLUE)) { Vector3f newColor = side->middle().color(); if (df & SIDF_MID_COLOR_RED) newColor.x = Reader_ReadByte(msgReader) / 255.f; if (df & SIDF_MID_COLOR_GREEN) newColor.y = Reader_ReadByte(msgReader) / 255.f; if (df & SIDF_MID_COLOR_BLUE) newColor.z = Reader_ReadByte(msgReader) / 255.f; side->middle().setColor(newColor); } if (df & SIDF_MID_COLOR_ALPHA) { side->middle().setOpacity(Reader_ReadByte(msgReader) / 255.f); } if (df & (SIDF_BOTTOM_COLOR_RED | SIDF_BOTTOM_COLOR_GREEN | SIDF_BOTTOM_COLOR_BLUE)) { Vector3f newColor = side->bottom().color(); if (df & SIDF_BOTTOM_COLOR_RED) newColor.x = Reader_ReadByte(msgReader) / 255.f; if (df & SIDF_BOTTOM_COLOR_GREEN) newColor.y = Reader_ReadByte(msgReader) / 255.f; if (df & SIDF_BOTTOM_COLOR_BLUE) newColor.z = Reader_ReadByte(msgReader) / 255.f; side->bottom().setColor(newColor); } if (df & SIDF_MID_BLENDMODE) { side->middle().setBlendMode(blendmode_t(Reader_ReadInt32(msgReader))); } if (df & SIDF_FLAGS) { // The delta includes the entire lowest byte. dint sideFlags = Reader_ReadByte(msgReader); side->setFlags((side->flags() & ~0xff) | sideFlags, de::ReplaceFlags); } }