#pragma once #include #include #include #include #include "doomdata.h" #include "r_utility.h" #include "r_main.h" // Transform for a view position and its viewport // // World space uses map coordinates in the XY plane. Z is up. // Eye space means relative to viewer, Y is up and Z is into the screen. // Viewport means in normalized device coordinates (-1 to 1 range with perspective division). 0,0 is in the center of the viewport and Y is still up. // Screen means in final screen coordinates. 0,0 is the upper left corner and Y is down. Z is still 1/z. // class ViewPosTransform { public: DVector3 WorldToEye(const DVector3 &worldPoint) const; DVector3 WorldToViewport(const DVector3 &worldPoint) const { return EyeToViewport(WorldToEye(worldPoint)); } DVector3 WorldToScreen(const DVector3 &worldPoint) const { return EyeToScreen(WorldToEye(worldPoint)); } DVector3 EyeToViewport(const DVector3 &eyePoint) const; DVector3 EyeToScreen(const DVector3 &eyePoint) const { return ViewportToScreen(EyeToViewport(eyePoint)); } DVector3 ViewportToScreen(const DVector3 &viewportPoint) const; double ScreenXToEye(int x, double z) const; double ScreenYToEye(int y, double z) const; double NearZ() const { return 0.0078125; }; }; // Screen space coordinates for a wall class WallCoords { public: WallCoords() = default; WallCoords(const ViewPosTransform &transform, const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2); // True if transform and clip culled the wall bool Culled = true; // Plane for wall in eye space DVector3 PlaneNormal; double PlaneD = 0.0; // Z range of the wall in eye space double NearZ = 0.0; double FarZ = 0.0; // Screen space bounding box of the wall int ScreenX1 = 0; int ScreenX2 = 0; int ScreenY1 = 0; int ScreenY2 = 0; // Get the Y positions for the given column short Y1(int x) const; short Y2(int x) const; // Get the depth for a column double Z(int x) const; // Perspective correct interpolation from start to end (used to calculate texture coordinates) double VaryingX(int x, double start, double end) const; double VaryingY(int x, int y, double start, double end) const; private: static DVector3 Mix(const DVector3 &a, const DVector3 &b, double t); static double Mix(double a, double b, double t); ViewPosTransform Transform; DVector3 ScreenTopLeft; DVector3 ScreenTopRight; DVector3 ScreenBottomLeft; DVector3 ScreenBottomRight; double RcpDeltaScreenX = 0.0; double VaryingXScale = 1.0; double VaryingXOffset = 0.0; }; // Texture coordinates for a wall class WallTextureCoords { public: WallTextureCoords(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); double u1, u2; double v1, v2; private: void CalcU(FTexture *tex, const seg_t *line, side_t::ETexpart texpart); void CalcV(FTexture *tex, const seg_t *line, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); void CalcVTopPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset); void CalcVMidPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double vscale, double yoffset); void CalcVBottomPart(FTexture *tex, const seg_t *line, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset); }; // Clipping buffers used during rendering class RenderClipBuffer { public: void Clear(short left, short right); void MarkSegmentCulled(const WallCoords &wallCoords, int drawIndex); void ClipVertical(const WallCoords &wallCoords, int drawIndex); void ClipTop(const WallCoords &wallCoords, int drawIndex); void ClipBottom(const WallCoords &wallCoords, int drawIndex); bool IsSegmentCulled(short x1, short x2) const; void SetupSpriteClip(short x1, short x2, const DVector3 &pos, bool wallSprite); void RenderMaskedWalls(); short Top[MAXWIDTH]; short Bottom[MAXWIDTH]; std::function DrawMaskedWall; private: void AddDrawSegment(short x1, short x2, const WallCoords &wall, bool clipTop, bool clipBottom, int drawIndex); struct SolidSegment { SolidSegment(short x1, short x2) : X1(x1), X2(x2) { } short X1, X2; }; struct DrawSegment { short X1; short X2; int ClipOffset; bool ClipTop; bool ClipBottom; DVector3 PlaneNormal; double PlaneD; double NearZ; double FarZ; int DrawIndex; }; std::vector SolidSegments; std::vector DrawSegments; std::vector ClipValues; friend class VisibleSegmentsIterator; }; // Walks the visible segments in a range class VisibleSegmentsIterator { public: VisibleSegmentsIterator(const RenderClipBuffer &buffer, short startx, short endx); bool Step(); short X1; short X2; private: const std::vector &SolidSegments; short endx; int next = 0; }; // Class used to group sector ceilings/floors sharing common properties class VisiblePlaneKey { public: VisiblePlaneKey() { } VisiblePlaneKey(FTextureID picnum, FSWColormap *colormap, int lightlevel, secplane_t plane, const FTransform &xform) : Picnum(picnum), ColorMap(colormap), LightLevel(lightlevel), Plane(plane), Transform(xform) { } bool operator==(const VisiblePlaneKey &other) const { return Picnum == other.Picnum && LightLevel == other.LightLevel && Plane.fD() == other.Plane.fD(); } FTextureID Picnum; FSWColormap *ColorMap; int LightLevel; secplane_t Plane; FTransform Transform; }; // Visible plane to be rendered class VisiblePlane { public: VisiblePlane(const VisiblePlaneKey &key) { Clear(key); } void Clear(const VisiblePlaneKey &key) { Key = key; Left = viewwidth; Right = 0; for (int i = 0; i < MAXWIDTH; i++) { Top[i] = UnsetValue; Bottom[i] = 0; } } VisiblePlaneKey Key; enum { UnsetValue = 0x7fff }; short Left; short Right; short Top[MAXWIDTH]; short Bottom[MAXWIDTH]; std::unique_ptr Next; }; class RenderVisiblePlane { public: RenderVisiblePlane(VisiblePlane *plane, FTexture *tex); void Step(); double viewx; double viewy; double planeheight; double basexfrac; double baseyfrac; double xstepscale; double ystepscale; }; // Tracks plane locations and renders them class RenderPlanes { public: void Clear(); void MarkCeilingPlane(const VisiblePlaneKey &key, const RenderClipBuffer &clip, const WallCoords &wallCoords); void MarkFloorPlane(const VisiblePlaneKey &key, const RenderClipBuffer &clip, const WallCoords &wallCoords); void Render(); private: void RenderPlane(VisiblePlane *plane); void RenderSpan(int y, int x1, int x2, const VisiblePlaneKey &key, FTexture *texture, const RenderVisiblePlane &renderInfo); VisiblePlane *GetPlaneWithUnsetRange(const VisiblePlaneKey &key, int x0, int x1); VisiblePlane *GetPlane(const VisiblePlaneKey &key); std::unique_ptr AllocPlane(const VisiblePlaneKey &key); static uint32_t Hash(const VisiblePlaneKey &key) { return ((unsigned)((key.Picnum.GetIndex()) * 3 + (key.LightLevel) + (FLOAT2FIXED(key.Plane.fD())) * 7) & (NumBuckets - 1)); } enum { NumBuckets = 128 /* must be a power of 2 */ }; std::unique_ptr PlaneBuckets[NumBuckets]; std::vector> FreePlanes; }; // Renders a wall texture class RenderWall { public: void Render(const RenderClipBuffer &clip); void RenderMasked(short x1, short x2, const short *clipTop, const short *clipBottom); WallCoords Coords; const seg_t *Line; side_t::ETexpart Texpart; double TopZ; double BottomZ; double UnpeggedCeil; FSWColormap *Colormap; bool Masked; private: FTexture *GetTexture(); int GetShade(); float GetLight(short x); }; // Sprite thing to be rendered class VisibleSprite { public: VisibleSprite(AActor *actor, const DVector3 &eyePos); void Render(RenderClipBuffer *clip); private: AActor *Actor; DVector3 EyePos; FTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX); visstyle_t GetSpriteVisStyle(AActor *thing, double z); friend class RenderBsp; // For sorting }; // DScreen accelerated sprite to be rendered class ScreenSprite { public: void Render(); FTexture *Pic = nullptr; double X1 = 0.0; double Y1 = 0.0; double Width = 0.0; double Height = 0.0; FRemapTable *Translation = nullptr; bool Flip = false; visstyle_t visstyle; uint32_t FillColor = 0; FDynamicColormap *Colormap = nullptr; }; // Renders a BSP tree in a scene class RenderBsp { public: void Render(); void RenderScreenSprites(); private: void RenderNode(void *node); void RenderSubsector(subsector_t *sub); void AddLine(seg_t *line, sector_t *frontsector); void AddSprite(AActor *thing); void AddWallSprite(AActor *thing); bool IsThingCulled(AActor *thing); void RenderMaskedObjects(); void RenderPlayerSprites(); void RenderPlayerSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac); int PointOnSide(const DVector2 &pos, const node_t *node); // Checks BSP node/subtree bounding box. // Returns true if some part of the bbox might be visible. bool CheckBBox(float *bspcoord); ViewPosTransform Transform; RenderClipBuffer Clip; RenderPlanes Planes; std::vector VisibleSprites; std::vector VisibleMaskedWalls; std::vector ScreenSprites; const int BaseXCenter = 160; const int BaseYCenter = 100; };