void PolyCull::MarkSegmentCulled(angle_t startAngle, angle_t endAngle) { if (startAngle > endAngle) { MarkSegmentCulled(startAngle, ANGLE_MAX); MarkSegmentCulled(0, endAngle); return; } int count = (int)SolidSegments.size(); int cur = 0; while (cur < count) { if (SolidSegments[cur].Start <= startAngle && SolidSegments[cur].End >= endAngle) // Already fully marked { return; } else if (SolidSegments[cur].End >= startAngle && SolidSegments[cur].Start <= endAngle) // Merge segments { // Find last segment int merge = cur; while (merge + 1 != count && SolidSegments[merge + 1].Start <= endAngle) merge++; // Apply new merged range SolidSegments[cur].Start = MIN(SolidSegments[cur].Start, startAngle); SolidSegments[cur].End = MAX(SolidSegments[merge].End, endAngle); // Remove additional segments we merged with if (merge > cur) SolidSegments.erase(SolidSegments.begin() + (cur + 1), SolidSegments.begin() + (merge + 1)); return; } else if (SolidSegments[cur].Start > startAngle) // Insert new segment { SolidSegments.insert(SolidSegments.begin() + cur, { startAngle, endAngle }); return; } cur++; } SolidSegments.push_back({ startAngle, endAngle }); #if 0 count = (int)SolidSegments.size(); for (int i = 1; i < count; i++) { if (SolidSegments[i - 1].Start >= SolidSegments[i].Start || SolidSegments[i - 1].End >= SolidSegments[i].Start || SolidSegments[i - 1].End + 1 == SolidSegments[i].Start || SolidSegments[i].Start > SolidSegments[i].End) { I_FatalError("MarkSegmentCulled is broken!"); } } #endif }
void PolyCull::InvertSegments() { TempInvertSolidSegments.swap(SolidSegments); ClearSolidSegments(); angle_t cur = 0; for (const auto &segment : TempInvertSolidSegments) { if (cur < segment.Start) MarkSegmentCulled(cur, segment.Start - 1); cur = segment.End + 1; } if (cur < ANGLE_MAX) MarkSegmentCulled(cur, ANGLE_MAX); }
void PolyCull::CullSubsector(subsector_t *sub) { // Check if subsector is clipped entirely by the portal clip plane bool visible = false; for (uint32_t i = 0; i < sub->numlines; i++) { seg_t *line = &sub->firstline[i]; if (PortalClipPlane.A * line->v1->fX() + PortalClipPlane.B * line->v1->fY() + PortalClipPlane.D > 0.0) { visible = true; break; } } if (!visible) return; // Update sky heights for the scene if (!FirstSkyHeight) { MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0()); MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0()); } else { MaxCeilingHeight = sub->sector->ceilingplane.Zat0(); MinFloorHeight = sub->sector->floorplane.Zat0(); FirstSkyHeight = false; } // Mark that we need to render this PvsSectors.push_back(sub); // Update culling info for further bsp clipping for (uint32_t i = 0; i < sub->numlines; i++) { seg_t *line = &sub->firstline[i]; if ((line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) && line->backsector == nullptr) { // Skip lines not facing viewer DVector2 pt1 = line->v1->fPos() - PolyRenderer::Instance()->Viewpoint.Pos; DVector2 pt2 = line->v2->fPos() - PolyRenderer::Instance()->Viewpoint.Pos; if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) continue; // Skip line if entirely behind portal clipping plane if ((PortalClipPlane.A * line->v1->fX() + PortalClipPlane.B * line->v1->fY() + PortalClipPlane.D <= 0.0) || (PortalClipPlane.A * line->v2->fX() + PortalClipPlane.B * line->v2->fY() + PortalClipPlane.D <= 0.0)) { continue; } angle_t angle1, angle2; if (GetAnglesForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), angle1, angle2)) { MarkSegmentCulled(angle1, angle2); } } } }
void PolyCull::MarkViewFrustum() { // Clips things outside the viewing frustum. auto &viewpoint = PolyRenderer::Instance()->Viewpoint; auto &viewwindow = PolyRenderer::Instance()->Viewwindow; double tilt = fabs(viewpoint.Angles.Pitch.Degrees); if (tilt > 46.0) // If the pitch is larger than this you can look all around return; double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(viewwindow.WidescreenRatio) / 90.0; angle_t a1 = DAngle(floatangle).BAMs(); if (a1 < ANGLE_180) { MarkSegmentCulled(AngleToPseudo(viewpoint.Angles.Yaw.BAMs() + a1), AngleToPseudo(viewpoint.Angles.Yaw.BAMs() - a1)); } }
void PolyCull::CullNode(void *node) { while (!((size_t)node & 1)) // Keep going until found a subsector { node_t *bsp = (node_t *)node; // Decide which side the view point is on. int side = PointOnSide(ViewPos, bsp); // Recursively divide front space (toward the viewer). CullNode(bsp->children[side]); // Possibly divide back space (away from the viewer). side ^= 1; if (!CheckBBox(bsp->bbox[side])) return; node = bsp->children[side]; } // Mark that we need to render this subsector_t *sub = (subsector_t *)((BYTE *)node - 1); MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0()); MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0()); PvsSectors.push_back(sub); // Update culling info for further bsp clipping for (uint32_t i = 0; i < sub->numlines; i++) { seg_t *line = &sub->firstline[i]; if ((line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) && line->backsector == nullptr) { int sx1, sx2; if (GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2)) { MarkSegmentCulled(sx1, sx2); } } } }
void PolyCull::CullSubsector(subsector_t *sub) { // Check if subsector is clipped entirely by the portal clip plane bool visible = false; for (uint32_t i = 0; i < sub->numlines; i++) { seg_t *line = &sub->firstline[i]; if (PortalClipPlane.A * line->v1->fX() + PortalClipPlane.B * line->v1->fY() + PortalClipPlane.D > 0.0) { visible = true; break; } } if (!visible) return; // Update sky heights for the scene if (!FirstSkyHeight) { MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0()); MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0()); } else { MaxCeilingHeight = sub->sector->ceilingplane.Zat0(); MinFloorHeight = sub->sector->floorplane.Zat0(); FirstSkyHeight = false; } uint32_t subsectorDepth = (uint32_t)PvsSectors.size(); // Mark that we need to render this PvsSectors.push_back(sub); PvsLineStart.push_back(NextPvsLineStart); DVector3 viewpos = PolyRenderer::Instance()->Viewpoint.Pos; // Update culling info for further bsp clipping for (uint32_t i = 0; i < sub->numlines; i++) { seg_t *line = &sub->firstline[i]; // Skip lines not facing viewer DVector2 pt1 = line->v1->fPos() - viewpos; DVector2 pt2 = line->v2->fPos() - viewpos; if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) { PvsLineVisible[NextPvsLineStart++] = false; continue; } // Skip line if entirely behind portal clipping plane if ((PortalClipPlane.A * line->v1->fX() + PortalClipPlane.B * line->v1->fY() + PortalClipPlane.D <= 0.0) || (PortalClipPlane.A * line->v2->fX() + PortalClipPlane.B * line->v2->fY() + PortalClipPlane.D <= 0.0)) { PvsLineVisible[NextPvsLineStart++] = false; continue; } angle_t angle1, angle2; bool lineVisible = GetAnglesForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), angle1, angle2); if (lineVisible && line->backsector == nullptr) { MarkSegmentCulled(angle1, angle2); } // Mark if this line was visible PvsLineVisible[NextPvsLineStart++] = lineVisible; } if (!SectorSeen[sub->sector->Index()]) { SectorSeen[sub->sector->Index()] = true; SeenSectors.push_back(sub->sector); } SubsectorDepths[sub->Index()] = subsectorDepth; }