mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-12-03 08:52:59 +00:00
345 lines
9.3 KiB
C++
345 lines
9.3 KiB
C++
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#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<void(short x1, short x2, int drawIndex, const short *clipTop, const short *clipBottom)> 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<SolidSegment> SolidSegments;
|
|
std::vector<DrawSegment> DrawSegments;
|
|
std::vector<short> 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<RenderClipBuffer::SolidSegment> &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<VisiblePlane> 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<VisiblePlane> 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<VisiblePlane> PlaneBuckets[NumBuckets];
|
|
std::vector<std::unique_ptr<VisiblePlane>> 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<VisibleSprite> VisibleSprites;
|
|
std::vector<RenderWall> VisibleMaskedWalls;
|
|
std::vector<ScreenSprite> ScreenSprites;
|
|
|
|
const int BaseXCenter = 160;
|
|
const int BaseYCenter = 100;
|
|
};
|