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