102 lines
3.3 KiB
C++
102 lines
3.3 KiB
C++
|
|
#include "render/scene.h"
|
|||
|
|
|
|||
|
|
namespace
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
// Запас за краями кадра, чтобы не отсекать спрайты, торчащие внутрь.
|
|||
|
|
constexpr int CULL_MARGIN_X = TILE_W;
|
|||
|
|
constexpr int CULL_MARGIN_Y = WALL_H + TILE_H;
|
|||
|
|
|
|||
|
|
inline bool OffScreen(int sx, int sy)
|
|||
|
|
{
|
|||
|
|
return sx < -CULL_MARGIN_X || sx > INTERNAL_W + CULL_MARGIN_X ||
|
|||
|
|
sy < -CULL_MARGIN_Y || sy > INTERNAL_H + CULL_MARGIN_Y;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace
|
|||
|
|
|
|||
|
|
void Scene::Begin(const iso::Camera& cam)
|
|||
|
|
{
|
|||
|
|
items.clear();
|
|||
|
|
cam.ScreenOffset(ox, oy);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Scene::Add(const Sprite* spr, Vec2 world, int pixelLift, float depthBias)
|
|||
|
|
{
|
|||
|
|
const Vec2 s = iso::WorldToScreen(world);
|
|||
|
|
const int sx = int(std::lround(s.x)) + ox;
|
|||
|
|
const int sy = int(std::lround(s.y)) + oy - pixelLift;
|
|||
|
|
if (OffScreen(sx, sy)) return;
|
|||
|
|
|
|||
|
|
iso::DrawItem it;
|
|||
|
|
it.depth = iso::Depth(world) + depthBias;
|
|||
|
|
it.spr = spr;
|
|||
|
|
it.x = sx;
|
|||
|
|
it.y = sy;
|
|||
|
|
items.push_back(it);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Scene::AddShaded(const Sprite* spr, Vec2 world, const ShadeQuad& quad, int pixelLift,
|
|||
|
|
float depthBias)
|
|||
|
|
{
|
|||
|
|
const Vec2 s = iso::WorldToScreen(world);
|
|||
|
|
const int sx = int(std::lround(s.x)) + ox;
|
|||
|
|
const int sy = int(std::lround(s.y)) + oy - pixelLift;
|
|||
|
|
if (OffScreen(sx, sy)) return;
|
|||
|
|
|
|||
|
|
iso::DrawItem it;
|
|||
|
|
it.depth = iso::Depth(world) + depthBias;
|
|||
|
|
it.spr = spr;
|
|||
|
|
it.x = sx;
|
|||
|
|
it.y = sy;
|
|||
|
|
it.shaded = true;
|
|||
|
|
it.quad = quad;
|
|||
|
|
items.push_back(it);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void Scene::Flush(Framebuffer& fb)
|
|||
|
|
{
|
|||
|
|
iso::SortByDepth(items);
|
|||
|
|
iso::FlushDrawList(fb, items);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Ниже этого уровня рампа даёт ступень 0, то есть чистый чёрный.
|
|||
|
|
static const float DARK_CUTOFF = 0.5f / float(RAMP_LEVELS - 1);
|
|||
|
|
|
|||
|
|
void DrawFloor(Framebuffer& fb, const SpriteSet& sprites, const Tilemap& map, int ox, int oy,
|
|||
|
|
const Lighting& light, const BloodField* blood)
|
|||
|
|
{
|
|||
|
|
for (int ty = 0; ty < MAP_H; ++ty)
|
|||
|
|
{
|
|||
|
|
for (int tx = 0; tx < MAP_W; ++tx)
|
|||
|
|
{
|
|||
|
|
if (map.IsWall(tx, ty)) continue; // под стеной пол не виден
|
|||
|
|
if (light.MaxCornerOf(tx, ty) < DARK_CUTOFF) continue;
|
|||
|
|
|
|||
|
|
const Vec2 c = iso::WorldToScreen({tx + 0.5f, ty + 0.5f});
|
|||
|
|
const int sx = int(std::lround(c.x)) + ox;
|
|||
|
|
const int sy = int(std::lround(c.y)) + oy;
|
|||
|
|
if (OffScreen(sx, sy)) continue;
|
|||
|
|
|
|||
|
|
const Sprite* stained = blood ? blood->At(tx, ty) : nullptr;
|
|||
|
|
const Sprite& s = stained ? *stained : sprites.FloorTile(tx, ty);
|
|||
|
|
fb.BlitShaded(s, sx, sy, light.QuadFor(tx, ty, TILE_H - 1));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void CollectWalls(Scene& scene, const SpriteSet& sprites, const Tilemap& map,
|
|||
|
|
const Lighting& light)
|
|||
|
|
{
|
|||
|
|
for (int ty = 0; ty < MAP_H; ++ty)
|
|||
|
|
for (int tx = 0; tx < MAP_W; ++tx)
|
|||
|
|
{
|
|||
|
|
if (!map.IsWall(tx, ty)) continue;
|
|||
|
|
if (light.MaxCornerOf(tx, ty) < DARK_CUTOFF) continue;
|
|||
|
|
// clampRow = TILE_H-1: вертикальные грани светятся как нижняя кромка
|
|||
|
|
// крыши, а не как экстраполяция патча на 32 строки вниз.
|
|||
|
|
scene.AddShaded(&sprites.wall, {tx + 0.5f, ty + 0.5f},
|
|||
|
|
light.QuadFor(tx, ty, TILE_H - 1));
|
|||
|
|
}
|
|||
|
|
}
|