Beispiel #1
0
/**
 * 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;
}
Beispiel #2
0
/**
 * @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;
}
Beispiel #3
0
/**
 * 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;
}
Beispiel #4
0
    /**
     * 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();
    }
Beispiel #5
0
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;
}
Beispiel #7
0
/// @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;
}
Beispiel #8
0
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);
    }
}