extract WorldRenderer

This commit is contained in:
2026-08-05 20:53:43 +02:00
parent dc83add5c6
commit e5dcb9de5f
10 changed files with 1531 additions and 1342 deletions

View File

@@ -61,6 +61,7 @@
#include "VisualsConfig.h"
#include "WorldCamera.h"
#include "WorldCoordinates.h"
#include "WorldRenderer.h"
struct Command;
struct ParsedReplay;
@@ -68,19 +69,6 @@ class ItemIconCache;
class ReplayPlayer;
class Simulation;
class QPainter;
class QSvgRenderer;
struct QPointCompare
{
bool operator()(const QPoint& a, const QPoint& b) const
{
if (a.x() != b.x()) { return a.x() < b.x(); }
return a.y() < b.y();
}
};
// Tunnel entries/exits indexed by their single-cell tile (REQ-BLD-TUNNEL-MODE).
using TunnelTileMap = std::map<QPoint, TunnelTileInfo, QPointCompare>;
class GameWorldView : public QOpenGLWidget,
public CombinedEventHandler<BeamFiredEvent,
@@ -159,29 +147,8 @@ private:
// Used to pre-validate placements whose UI follow-up depends on success.
bool canAfford(BuildingType type) const;
// World-space drawing takes the frame's transform (see getCoordinates()) rather
// than reaching for the scroll position and viewport size itself; the
// screen-space draws below need neither.
void drawTiles(QPainter& painter, const WorldCoordinates& coordinates);
void drawPortItems(QPainter& painter, const WorldCoordinates& coordinates);
void drawBuildings(QPainter& painter, const WorldCoordinates& coordinates);
void drawSelectionHighlights(QPainter& painter, const WorldCoordinates& coordinates);
void drawCopyConfigFeedback(QPainter& painter, const WorldCoordinates& coordinates);
void drawStations(QPainter& painter, const WorldCoordinates& coordinates);
void drawBeltItems(QPainter& painter, const WorldCoordinates& coordinates);
// Draws a single item centered at widget-space `center`, spanning `halfPx` in
// each direction (a half-tile). Uses the item's icon when one exists
// (REQ-UI-ITEM-ICON), otherwise falls back to the colored square from
// visuals.toml. Shared by drawBeltItems and drawPortItems.
void drawWorldItem(QPainter& painter, const std::string& itemId,
QPointF center, float halfPx);
void drawDebris(QPainter& painter, const WorldCoordinates& coordinates);
void drawShips(QPainter& painter, const WorldCoordinates& coordinates);
void drawDebugSensorRanges(QPainter& painter, const WorldCoordinates& coordinates);
void drawDebugTargetLines(QPainter& painter, const WorldCoordinates& coordinates);
void drawDebugOverlay(QPainter& painter);
void drawBeams(QPainter& painter, const WorldCoordinates& coordinates);
void drawOverlays(QPainter& painter, const WorldCoordinates& coordinates);
// Screen-anchored chrome, drawn after the world (see WorldRenderer): these
// need no world transform, which is exactly why they stayed here.
void drawScreenSpace(QPainter& painter);
// Vignette-style border shown while the game is paused (speed 0x, REQ-UI-PAUSE-BORDER)
// to make the paused state hard to miss: a black frame whose alpha fades from 50% at
@@ -197,17 +164,14 @@ private:
void drawVignetteBorder(QPainter& painter, const QColor& edgeColor);
void drawReplayOverlay(QPainter& painter);
// Gathers the interaction state the renderer needs for this frame.
WorldRenderFrame makeRenderFrame() const;
// The world <-> widget transform for the current viewport size and scroll
// position. Cheap to build and deliberately not cached: it is a snapshot that
// a resize or a scroll invalidates, so every user takes a fresh one.
WorldCoordinates getCoordinates() const;
// Widget-space rectangle covering a building or construction site's footprint,
// or nullopt if the id resolves to neither. Shared by the selection highlight
// and the copy-settings feedback (REQ-BLD-COPY-CONFIG-FEEDBACK).
std::optional<QRectF> footprintWidgetRect(const WorldCoordinates& coordinates,
BuildingId id) const;
float getAsteroidLeftEdge() const;
float getEnemyStationRightEdge() const;
// The camera's current pan limits, read fresh from the simulation each frame:
@@ -218,36 +182,9 @@ private:
bool canPlaceBuildingHere(BuildingType type, QPoint anchor, Rotation rot) const;
std::optional<BuildingId> buildingAtTile(QPoint tile) const;
std::optional<BuildingId> siteAtTile(QPoint tile) const;
// Ids of all buildings and construction sites whose footprint intersects
// the tile box spanned by the two (unordered) corner tiles.
std::vector<BuildingId> buildingsInBox(QPoint cornerA, QPoint cornerB) const;
void drawPortGlyph(QPainter& painter, const WorldCoordinates& coordinates,
QPoint tile, Rotation direction, const QColor& color,
bool centered);
void drawBuildingGhost(QPainter& painter, const WorldCoordinates& coordinates,
BuildingType type,
QPoint anchorTile, Rotation rotation, bool valid,
bool showPortTargetGlyphs);
// Loads the per-building world icons (REQ-UI-WORLD-ICON) from
// <configDir>/../icons/buildings once at construction. Only the building
// types with a world icon are loaded (production buildings, HQ, stations);
// belts, splitters, and tunnels are deliberately excluded so their
// orientation stays readable. The SVG's chip background is stripped; the
// glyph is pre-rendered in both white and dark ink for auto-contrast.
void loadBuildingIcons(const std::string& configDir);
// Draws a building's world icon glyph centered in box, choosing the white or
// dark pre-rendered variant by fill luminance so it stays legible. Returns
// false if the type has no world icon (caller falls back to the text glyph).
bool drawBuildingIcon(QPainter& painter, const WorldCoordinates& coordinates,
BuildingType type,
const QRectF& box, const QColor& fill) const;
void placeBlueprintAtTile(QPoint center);
std::optional<QVector2D> entityPosition(entt::entity entity) const;
// Drops despawned or fully-collected debris from the selection
// (REQ-UI-DEBRIS-CLICK-SELECT). Called each frame from onFrame().
void pruneDespawnedDebris();
@@ -267,17 +204,6 @@ private:
// ghost tile, rotation, and sub-tile cursor position, storing both on the build
// mode controller. Only meaningful in tunnel mode (REQ-BLD-TUNNEL-MODE).
void updateTunnelGhost();
// Indexes every tunnel entry/exit — built or still a construction site — by its
// single-cell tile. Shared by the placement preview and the selection highlight.
TunnelTileMap collectTunnelTiles() const;
// Wraps a tunnel tile index in the lookup functor the TunnelCompletion helpers
// take. The returned functor references `tunnels`, which must outlive it.
static TunnelLookup makeTunnelLookup(const TunnelTileMap& tunnels);
// Draws the green connection highlight for every selected tunnel end that has a
// matching end (REQ-BLD-TUNNEL-SELECT-HIGHLIGHT).
void drawSelectedTunnelConnections(QPainter& painter,
const WorldCoordinates& coordinates);
// Belt drag placement (REQ-BLD-BELT-DRAG).
// Recomputes the drag path from its anchor to cursorTile using the current ghost
// orientation, and stores it on the build mode controller.
@@ -298,12 +224,6 @@ private:
// The mode transitions themselves live on m_buildMode.
void rotateGhost(bool clockwise);
struct ActiveBeam
{
BeamFiredEvent event;
QVector2D targetOffset;
};
// Beam lifetime in game ticks so beams freeze with the simulation when
// paused or slowed, instead of fading on wall-clock time (REQ-SHP-FIRING-BEAM).
static constexpr Tick kBeamLifetimeTicks = secondsToTicks(0.3);
@@ -312,22 +232,6 @@ private:
const GameConfig* m_config;
const VisualsConfig* m_visuals;
// World icon glyph renderers per building type (REQ-UI-WORLD-ICON), in a
// white and a dark variant so drawBuildingIcon can auto-contrast against the
// building's fill. Rendered as vector at the view scale each draw so they
// stay crisp. Populated once by loadBuildingIcons().
struct BuildingIconRenderers
{
std::unique_ptr<QSvgRenderer> white;
std::unique_ptr<QSvgRenderer> dark;
};
std::map<BuildingType, BuildingIconRenderers> m_buildingIcons;
// Per-item icon cache (REQ-UI-ITEM-ICON), shared window-wide and owned by
// MainWindow. Shared draw path for belt and port items; pixmaps are cached
// per target size.
ItemIconCache* m_itemIcons;
// Funnels all player input into the single Simulation::apply chokepoint.
CommandManager m_commandManager;
// A Reset command was enqueued; reset the view after the next drain applies it.
@@ -336,6 +240,10 @@ private:
// live input is ignored (the CommandManager is in replay mode).
std::unique_ptr<ReplayPlayer> m_replayPlayer;
// Draws the world; this widget supplies the interaction state each frame and
// keeps only the screen-anchored chrome for itself.
std::unique_ptr<WorldRenderer> m_renderer;
TickDriver m_tickDriver;
QElapsedTimer m_frameTimer;
std::mt19937 m_rng;
@@ -367,11 +275,6 @@ private:
// pasted onto it (REQ-BLD-COPY-CONFIG-FEEDBACK). remainingMs counts down in
// wall-clock time so the flash plays at a fixed length regardless of game speed
// (and while paused).
struct CopyConfigFlash
{
BuildingId id;
qint64 remainingMs;
};
std::vector<CopyConfigFlash> m_copyConfigFlashes;
static constexpr qint64 kCopyFlashDurationMs = 300;