You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

374 lines
9.7 KiB

/**
* @file render.cpp
*
* Implementation of functionality for rendering the level tiles.
*/
#include "all.h"
DEVILUTION_BEGIN_NAMESPACE
#define NO_OVERDRAW
enum {
RT_SQUARE,
RT_TRANSPARENT,
RT_LTRIANGLE,
RT_RTRIANGLE,
RT_LTRAPEZOID,
RT_RTRAPEZOID
};
/** Specifies the draw masks used to render transparency of the right side of tiles. */
static DWORD RightMask[TILE_HEIGHT] = {
0xEAAAAAAA, 0xF5555555,
0xFEAAAAAA, 0xFF555555,
0xFFEAAAAA, 0xFFF55555,
0xFFFEAAAA, 0xFFFF5555,
0xFFFFEAAA, 0xFFFFF555,
0xFFFFFEAA, 0xFFFFFF55,
0xFFFFFFEA, 0xFFFFFFF5,
0xFFFFFFFE, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF
};
/** Specifies the draw masks used to render transparency of the left side of tiles. */
static DWORD LeftMask[TILE_HEIGHT] = {
0xAAAAAAAB, 0x5555555F,
0xAAAAAABF, 0x555555FF,
0xAAAAABFF, 0x55555FFF,
0xAAAABFFF, 0x5555FFFF,
0xAAABFFFF, 0x555FFFFF,
0xAABFFFFF, 0x55FFFFFF,
0xABFFFFFF, 0x5FFFFFFF,
0xBFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF
};
/** Specifies the draw masks used to render transparency of wall tiles. */
static DWORD WallMask[TILE_HEIGHT] = {
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555,
0xAAAAAAAA, 0x55555555
};
static DWORD SolidMask[TILE_HEIGHT] = {
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF,
0xFFFFFFFF, 0xFFFFFFFF
};
static DWORD RightFoliageMask[TILE_HEIGHT] = {
0xFFFFFFFF, 0x3FFFFFFF,
0x0FFFFFFF, 0x03FFFFFF,
0x00FFFFFF, 0x003FFFFF,
0x000FFFFF, 0x0003FFFF,
0x0000FFFF, 0x00003FFF,
0x00000FFF, 0x000003FF,
0x000000FF, 0x0000003F,
0x0000000F, 0x00000003,
0x00000000, 0x00000003,
0x0000000F, 0x0000003F,
0x000000FF, 0x000003FF,
0x00000FFF, 0x00003FFF,
0x0000FFFF, 0x0003FFFF,
0x000FFFFF, 0x003FFFFF,
0x00FFFFFF, 0x03FFFFFF,
0x0FFFFFFF, 0x3FFFFFFF,
};
static DWORD LeftFoliageMask[TILE_HEIGHT] = {
0xFFFFFFFF, 0xFFFFFFFC,
0xFFFFFFF0, 0xFFFFFFC0,
0xFFFFFF00, 0xFFFFFC00,
0xFFFFF000, 0xFFFFC000,
0xFFFF0000, 0xFFFC0000,
0xFFF00000, 0xFFC00000,
0xFF000000, 0xFC000000,
0xF0000000, 0xC0000000,
0x00000000, 0xC0000000,
0xF0000000, 0xFC000000,
0xFF000000, 0xFFC00000,
0xFFF00000, 0xFFFC0000,
0xFFFF0000, 0xFFFFC000,
0xFFFFF000, 0xFFFFFC00,
0xFFFFFF00, 0xFFFFFFC0,
0xFFFFFFF0, 0xFFFFFFFC,
};
inline static int count_leading_zeros(DWORD mask) {
// Note: This assumes that the argument is not zero,
// which means there is at least one bit set.
#if defined(__GNUC__) || defined(__clang__)
return __builtin_clz(mask);
#else
int i;
for (i = 0; (mask & 0x80000000) == 0; i++, mask <<= 1);
return i;
#endif
}
template <typename F>
void foreach_set_bit(DWORD mask, const F& f) {
int i = 0;
while (mask != 0) {
int z = count_leading_zeros(mask);
i += z, mask <<= z;
for (; mask & 0x80000000; i++, mask <<= 1)
f(i);
}
}
inline static void RenderLine(BYTE **dst, BYTE **src, int n, BYTE *tbl, DWORD mask)
{
#ifdef NO_OVERDRAW
if (*dst < gpBufStart || *dst > gpBufEnd) {
goto skip;
}
#endif
if (mask == 0xFFFFFFFF) {
if (light_table_index == lightmax) {
memset(*dst, 0, n);
} else if (light_table_index == 0) {
memcpy(*dst, *src, n);
} else {
for (int i = 0; i < n; i++) {
(*dst)[i] = tbl[(*src)[i]];
}
}
} else {
// The number of iterations is anyway limited by the size of the mask.
// So we can limit it by ANDing the mask with another mask that only keeps
// iterations that are lower than n. We can now avoid testing if i < n
// at every loop iteration.
mask &= ((((DWORD)1) << n) - 1) << ((sizeof(DWORD) * CHAR_BIT) - n);
if (light_table_index == lightmax) {
foreach_set_bit(mask, [=] (int i) { (*dst)[i] = 0; });
} else if (light_table_index == 0) {
foreach_set_bit(mask, [=] (int i) { (*dst)[i] = (*src)[i]; });
} else {
foreach_set_bit(mask, [=] (int i) { (*dst)[i] = tbl[(*src)[i]]; });
}
}
skip:
(*src) += n;
(*dst) += n;
}
#if defined(__clang__) || defined(__GNUC__)
__attribute__((no_sanitize("shift-base")))
#endif
/**
* @brief Blit current world CEL to the given buffer
* @param pBuff Output buffer
*/
void RenderTile(BYTE *pBuff)
{
int i, j;
char c, v, tile;
BYTE *src, *dst, *tbl;
DWORD m, *mask, *pFrameTable;
dst = pBuff;
pFrameTable = (DWORD *)pDungeonCels;
src = &pDungeonCels[SDL_SwapLE32(pFrameTable[level_cel_block & 0xFFF])];
tile = (level_cel_block & 0x7000) >> 12;
tbl = &pLightTbl[256 * light_table_index];
mask = &SolidMask[TILE_HEIGHT - 1];
if (cel_transparency_active) {
if (arch_draw_type == 0) {
mask = &WallMask[TILE_HEIGHT - 1];
}
if (arch_draw_type == 1 && tile != RT_LTRIANGLE) {
c = block_lvid[level_piece_id];
if (c == 1 || c == 3) {
mask = &LeftMask[TILE_HEIGHT - 1];
}
}
if (arch_draw_type == 2 && tile != RT_RTRIANGLE) {
c = block_lvid[level_piece_id];
if (c == 2 || c == 3) {
mask = &RightMask[TILE_HEIGHT - 1];
}
}
} else if (arch_draw_type && cel_foliage_active) {
if (tile != RT_TRANSPARENT) {
return;
}
if (arch_draw_type == 1) {
mask = &LeftFoliageMask[TILE_HEIGHT - 1];
}
if (arch_draw_type == 2) {
mask = &RightFoliageMask[TILE_HEIGHT - 1];
}
}
#ifdef _DEBUG
if (GetAsyncKeyState(DVL_VK_MENU) & 0x8000) {
mask = &SolidMask[TILE_HEIGHT - 1];
}
#endif
switch (tile) {
case RT_SQUARE:
for (i = TILE_HEIGHT; i != 0; i--, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
RenderLine(&dst, &src, TILE_WIDTH / 2, tbl, *mask);
}
break;
case RT_TRANSPARENT:
for (i = TILE_HEIGHT; i != 0; i--, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
m = *mask;
for (j = TILE_WIDTH / 2; j != 0; j -= v, v == TILE_WIDTH / 2 ? m = 0 : m <<= v) {
v = *src++;
if (v >= 0) {
RenderLine(&dst, &src, v, tbl, m);
} else {
v = -v;
dst += v;
}
}
}
break;
case RT_LTRIANGLE:
for (i = TILE_HEIGHT - 2; i >= 0; i -= 2, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
src += i & 2;
dst += i;
RenderLine(&dst, &src, TILE_WIDTH / 2 - i, tbl, *mask);
}
for (i = 2; i != TILE_WIDTH / 2; i += 2, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
src += i & 2;
dst += i;
RenderLine(&dst, &src, TILE_WIDTH / 2 - i, tbl, *mask);
}
break;
case RT_RTRIANGLE:
for (i = TILE_HEIGHT - 2; i >= 0; i -= 2, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
RenderLine(&dst, &src, TILE_WIDTH / 2 - i, tbl, *mask);
src += i & 2;
dst += i;
}
for (i = 2; i != TILE_HEIGHT; i += 2, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
RenderLine(&dst, &src, TILE_WIDTH / 2 - i, tbl, *mask);
src += i & 2;
dst += i;
}
break;
case RT_LTRAPEZOID:
for (i = TILE_HEIGHT - 2; i >= 0; i -= 2, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
src += i & 2;
dst += i;
RenderLine(&dst, &src, TILE_WIDTH / 2 - i, tbl, *mask);
}
for (i = TILE_HEIGHT / 2; i != 0; i--, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
RenderLine(&dst, &src, TILE_WIDTH / 2, tbl, *mask);
}
break;
case RT_RTRAPEZOID:
for (i = TILE_HEIGHT - 2; i >= 0; i -= 2, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
RenderLine(&dst, &src, TILE_WIDTH / 2 - i, tbl, *mask);
src += i & 2;
dst += i;
}
for (i = TILE_HEIGHT / 2; i != 0; i--, dst -= BUFFER_WIDTH + TILE_WIDTH / 2, mask--) {
RenderLine(&dst, &src, TILE_WIDTH / 2, tbl, *mask);
}
break;
}
}
/**
* @brief Render a black tile
* @param sx Back buffer coordinate
* @param sy Back buffer coordinate
*/
void world_draw_black_tile(int sx, int sy)
{
int i, j, k;
BYTE *dst;
if (sx >= SCREEN_X + SCREEN_WIDTH || sy >= SCREEN_Y + VIEWPORT_HEIGHT + TILE_WIDTH / 2)
return;
if (sx < SCREEN_X - (TILE_WIDTH - 4) || sy < SCREEN_Y)
return;
dst = &gpBuffer[sx + BUFFER_WIDTH * sy] + TILE_WIDTH / 2 - 2;
for (i = TILE_HEIGHT - 2, j = 1; i >= 0; i -= 2, j++, dst -= BUFFER_WIDTH + 2) {
if (dst < gpBufEnd)
memset(dst, 0, 4 * j);
}
dst += 4;
for (i = 2, j = TILE_HEIGHT / 2 - 1; i != TILE_HEIGHT; i += 2, j--, dst -= BUFFER_WIDTH - 2) {
if (dst < gpBufEnd)
memset(dst, 0, 4 * j);
}
}
/**
* Draws a half-transparent rectangle by blacking out odd pixels on odd lines,
* even pixels on even lines.
* @brief Render a transparent black rectangle
* @param sx Screen coordinate
* @param sy Screen coordinate
* @param width Rectangle width
* @param height Rectangle height
*/
void trans_rect(int sx, int sy, int width, int height)
{
int row, col;
BYTE *pix = &gpBuffer[SCREENXY(sx, sy)];
for (row = 0; row < height; row++) {
for (col = 0; col < width; col++) {
if ((row & 1 && col & 1) || (!(row & 1) && !(col & 1)))
*pix = 0;
pix++;
}
pix += BUFFER_WIDTH - width;
}
}
DEVILUTION_END_NAMESPACE