mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-13 21:39:20 +00:00
bcd1412f75
Refactor the monolithic TerritoryWebGLRenderer into a modular, extensible architecture that separates ground truth computation from rendering passes. This change also includes related improvements to game state management and hover information handling. WebGPU Architecture Refactor: - Extract all shaders to external .wgsl files (no inlined shaders) - Separate ground truth data management (GroundTruthData) from rendering - Create pass-based architecture with ComputePass and RenderPass interfaces - Implement compute passes: StateUpdatePass, DefendedClearPass, DefendedUpdatePass - Implement render pass: TerritoryRenderPass - Add TerritoryRenderer orchestrator with dependency-based execution ordering - Add WebGPUDevice for device initialization and management - Add ShaderLoader utility for loading .wgsl files via Vite ?raw imports Performance Optimizations: - Dependency order computed once at init (topological sort) - Early exit checks at orchestrator and pass levels - Bind groups rebuilt when textures/buffers are recreated - Zero per-frame allocations (reuse command encoders and staging buffers) Architecture Benefits: - Easy to extend with new compute/render passes (borders, temporal smoothing, etc.) - Clear separation between tick-based compute and frame-based rendering - All shaders in external files for better maintainability - Ground truth data computed once and reused by all passes Related Changes: - Add defended tile state support to GameMap (isDefended/setDefended) - Expose tileStateView() for direct GPU state access - Extract hover info logic to HoverInfo utility - Remove TerrainLayer (terrain now rendered by WebGPU territory pass) - Update GameRenderer to use transparent overlay canvas - Add viewOffset() method to TransformHandler Files: - Deleted: TerritoryWebGLRenderer.ts (1217 lines), TerrainLayer.ts (77 lines) - Added: 17 new files in webgpu/ directory structure - Updated: TerritoryLayer.ts, GameRenderer.ts, PlayerInfoOverlay.ts, GameMap.ts, GameView.ts, GameImpl.ts, TransformHandler.ts, vite-env.d.ts
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;
|
|
}
|