qzdoom-gpl/src/r_swrenderer2.h
2016-09-24 09:36:37 +02:00

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;
};