mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-03 13:30:53 +00:00
202f59a410
Replace Canvas 2D-based territory rendering with a WebGPU implementation for improved performance and scalability. Major changes: - Remove TerrainLayer: territory and terrain now rendered together in WebGPU - Add TerritoryWebGLRenderer: new WebGPU-based renderer (1162 lines) - GPU-authoritative state management with compute shaders - Separate uniform buffers for compute and render passes - Efficient incremental updates via scatter/gather compute passes - Full rebuild compute pass for palette/theme changes - Add defense post management: - Track defended state in GameMap (bit 12 in tile state) - Update defended tiles when defense posts are added/removed/moved - Update defended state when tiles change ownership - Extract hover detection into HoverInfo utility: - Centralized logic for player/unit/wilderness detection - Used by PlayerInfoOverlay for cleaner separation of concerns - Canvas architecture changes: - Main canvas now transparent (alpha: true) - WebGPU canvas renders background and territory - Overlay canvas renders UI elements on top - Performance optimizations: - Compute shaders run at simulation rate (tick), not frame rate - Incremental tile updates via pending tiles set - Palette signature tracking to avoid unnecessary rebuilds - Defense posts signature tracking for efficient updates Files changed: - 10 files changed, 1447 insertions(+), 752 deletions(-) - New: TerritoryWebGLRenderer.ts, HoverInfo.ts - Removed: TerrainLayer.ts - Modified: TerritoryLayer.ts (simplified from 710 to 250 lines)
74 lines
1.8 KiB
TypeScript
74 lines
1.8 KiB
TypeScript
import { UnitType } from "../../core/game/Game";
|
|
import { TileRef } from "../../core/game/GameMap";
|
|
import { GameView, PlayerView, UnitView } from "../../core/game/GameView";
|
|
|
|
export type HoverInfo = {
|
|
player: PlayerView | null;
|
|
unit: UnitView | null;
|
|
isWilderness: boolean;
|
|
isIrradiatedWilderness: boolean;
|
|
};
|
|
|
|
function euclideanDistWorld(
|
|
coord: { x: number; y: number },
|
|
tileRef: TileRef,
|
|
game: GameView,
|
|
): number {
|
|
const x = game.x(tileRef);
|
|
const y = game.y(tileRef);
|
|
const dx = coord.x - x;
|
|
const dy = coord.y - y;
|
|
return Math.sqrt(dx * dx + dy * dy);
|
|
}
|
|
|
|
function distSortUnitWorld(coord: { x: number; y: number }, game: GameView) {
|
|
return (a: UnitView, b: UnitView) => {
|
|
const distA = euclideanDistWorld(coord, a.tile(), game);
|
|
const distB = euclideanDistWorld(coord, b.tile(), game);
|
|
return distA - distB;
|
|
};
|
|
}
|
|
|
|
export function getHoverInfo(
|
|
game: GameView,
|
|
worldCoord: { x: number; y: number },
|
|
): HoverInfo {
|
|
const info: HoverInfo = {
|
|
player: null,
|
|
unit: null,
|
|
isWilderness: false,
|
|
isIrradiatedWilderness: false,
|
|
};
|
|
|
|
if (!game.isValidCoord(worldCoord.x, worldCoord.y)) {
|
|
return info;
|
|
}
|
|
|
|
const tile = game.ref(worldCoord.x, worldCoord.y);
|
|
const owner = game.owner(tile);
|
|
|
|
if (owner && owner.isPlayer()) {
|
|
info.player = owner as PlayerView;
|
|
return info;
|
|
}
|
|
|
|
if (owner && !owner.isPlayer() && game.isLand(tile)) {
|
|
info.isIrradiatedWilderness = game.hasFallout(tile);
|
|
info.isWilderness = !info.isIrradiatedWilderness;
|
|
return info;
|
|
}
|
|
|
|
if (!game.isLand(tile)) {
|
|
const units = game
|
|
.units(UnitType.Warship, UnitType.TradeShip, UnitType.TransportShip)
|
|
.filter((u) => euclideanDistWorld(worldCoord, u.tile(), game) < 50)
|
|
.sort(distSortUnitWorld(worldCoord, game));
|
|
|
|
if (units.length > 0) {
|
|
info.unit = units[0];
|
|
}
|
|
}
|
|
|
|
return info;
|
|
}
|