/** * Same as @ref R_OpenRange() but works with the "visual" (i.e., smoothed) plane * height coordinates rather than the "sharp" coordinates. * * @param side Line side to find the open range for. * * Return values: * @param bottom Bottom Z height is written here. Can be @c 0. * @param top Top Z height is written here. Can be @c 0. * * @return Height of the open range. * * @todo fixme: Should use the visual plane heights of sector clusters. */ static coord_t visOpenRange(LineSide const &side, coord_t *retBottom = 0, coord_t *retTop = 0) { Sector const *frontSec = side.sectorPtr(); Sector const *backSec = side.back().sectorPtr(); coord_t bottom; if(backSec && backSec->floor().heightSmoothed() > frontSec->floor().heightSmoothed()) { bottom = backSec->floor().heightSmoothed(); } else { bottom = frontSec->floor().heightSmoothed(); } coord_t top; if(backSec && backSec->ceiling().heightSmoothed() < frontSec->ceiling().heightSmoothed()) { top = backSec->ceiling().heightSmoothed(); } else { top = frontSec->ceiling().heightSmoothed(); } if(retBottom) *retBottom = bottom; if(retTop) *retTop = top; return top - bottom; }
/** * @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; }
/** * Should angle based light level deltas be applied? */ static bool useWallSectionLightLevelDeltas(LineSide const &side, int section) { // Disabled? if(rendLightWallAngle <= 0) return false; // Never if the surface's material was chosen as a HOM fix (lighting must // be consistent with that applied to the relative back sector plane). if(side.surface(section).hasFixMaterial() && side.hasSector() && side.back().hasSector()) { Sector &backSector = side.back().sector(); if(backSector.floor().height() < backSector.ceiling().height()) return false; } return true; }
/** * 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(); }
WallSpec WallSpec::fromMapSide(LineSide const &side, int section) // static { bool const isTwoSidedMiddle = (section == LineSide::Middle && !side.considerOneSided()); WallSpec spec(section); if(side.line().definesPolyobj() || isTwoSidedMiddle) { spec.flags &= ~WallSpec::ForceOpaque; spec.flags |= WallSpec::NoEdgeDivisions; } if(isTwoSidedMiddle) { if(viewPlayer && ((viewPlayer->shared.flags & (DDPF_NOCLIP|DDPF_CAMERA)) || !side.line().isFlagged(DDLF_BLOCKING))) spec.flags |= WallSpec::NearFade; spec.flags |= WallSpec::SortDynLights; } // Suppress the sky clipping in debug mode. if(devRendSkyMode) spec.flags &= ~WallSpec::SkyClip; if(side.line().definesPolyobj()) spec.flags |= WallSpec::NoFakeRadio; bool useLightLevelDeltas = useWallSectionLightLevelDeltas(side, section); if(!useLightLevelDeltas) spec.flags |= WallSpec::NoLightDeltas; // We can skip normal smoothing if light level delta smoothing won't be done. if(!useLightLevelDeltas || !rendLightWallAngleSmooth) spec.flags |= WallSpec::NoEdgeNormalSmoothing; return spec; }
/// @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()) { // Ensure we have up to date info about the material. MaterialSnapshot const &ms = side.middle().material().prepare(Rend_MapSurfaceMaterialSpec()); if(ignoreOpacity || (ms.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(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; }
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); } }