mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-14 07:54:50 +00:00
Display territory skins again (#3966)
## Description: Display territory skins (patterns) again. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: tryout33
This commit is contained in:
@@ -217,8 +217,13 @@ export class GameView {
|
||||
updatePalette(paletteData: Float32Array): void {
|
||||
this.renderer.updatePalette(paletteData);
|
||||
}
|
||||
addPlayers(players: PlayerStatic[], paletteData: Float32Array): void {
|
||||
this.renderer.addPlayers(players, paletteData);
|
||||
addPlayers(
|
||||
players: PlayerStatic[],
|
||||
paletteData: Float32Array,
|
||||
patternMeta: Float32Array,
|
||||
patternData: Uint8Array,
|
||||
): void {
|
||||
this.renderer.addPlayers(players, paletteData, patternMeta, patternData);
|
||||
}
|
||||
uploadRailroadState(data: Uint8Array): void {
|
||||
this.renderer.uploadRailroadState(data);
|
||||
@@ -328,6 +333,9 @@ export class GameView {
|
||||
setAltView(active: boolean): void {
|
||||
this.renderer.setAltView(active);
|
||||
}
|
||||
setShowPatterns(active: boolean): void {
|
||||
this.renderer.setShowPatterns(active);
|
||||
}
|
||||
setHighlightOwner(ownerID: number): void {
|
||||
this.renderer.setHighlightOwner(ownerID);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ export interface RenderSettings {
|
||||
passEnabled: {
|
||||
terrain: boolean;
|
||||
mapOverlay: boolean;
|
||||
territoryPatterns: boolean;
|
||||
structure: boolean;
|
||||
unit: boolean;
|
||||
name: boolean;
|
||||
|
||||
@@ -127,6 +127,8 @@ export class GPURenderer {
|
||||
|
||||
private paletteTex: WebGLTexture;
|
||||
private paletteData: Float32Array;
|
||||
private patternMetaTex: WebGLTexture;
|
||||
private patternDataTex: WebGLTexture;
|
||||
private canvas: HTMLCanvasElement;
|
||||
private settings: RenderSettings;
|
||||
private sceneTarget: RenderTarget;
|
||||
@@ -219,6 +221,26 @@ export class GPURenderer {
|
||||
filter: gl.NEAREST,
|
||||
});
|
||||
|
||||
this.patternMetaTex = createTexture2D(gl, {
|
||||
width: palW,
|
||||
height: 1,
|
||||
internalFormat: gl.RGBA32F,
|
||||
format: gl.RGBA,
|
||||
type: gl.FLOAT,
|
||||
data: new Float32Array(palW * 4),
|
||||
filter: gl.NEAREST,
|
||||
});
|
||||
|
||||
this.patternDataTex = createTexture2D(gl, {
|
||||
width: 1024,
|
||||
height: palW,
|
||||
internalFormat: gl.R8UI,
|
||||
format: gl.RED_INTEGER,
|
||||
type: gl.UNSIGNED_BYTE,
|
||||
data: new Uint8Array(palW * 1024),
|
||||
filter: gl.NEAREST,
|
||||
});
|
||||
|
||||
// --- Border compute (creates its own borderTex) ---
|
||||
// Need a temporary tileTex reference for border compute — we'll create
|
||||
// GPUResources first, then wire everything.
|
||||
@@ -259,7 +281,7 @@ export class GPURenderer {
|
||||
this.settings,
|
||||
);
|
||||
|
||||
// --- Territory (needs tileTex, trailTex, paletteTex) ---
|
||||
// --- Territory (needs tileTex, trailTex, paletteTex, patternTexs) ---
|
||||
this.territoryPass = new TerritoryPass(
|
||||
gl,
|
||||
mapW,
|
||||
@@ -267,6 +289,8 @@ export class GPURenderer {
|
||||
this.res.tileTex,
|
||||
this.res.trailTex,
|
||||
this.paletteTex,
|
||||
this.patternMetaTex,
|
||||
this.patternDataTex,
|
||||
this.settings,
|
||||
);
|
||||
|
||||
@@ -582,8 +606,43 @@ export class GPURenderer {
|
||||
}
|
||||
|
||||
/** Register late-arriving players (updates palette + NamePass lookup maps). */
|
||||
addPlayers(players: PlayerStatic[], paletteData: Float32Array): void {
|
||||
addPlayers(
|
||||
players: PlayerStatic[],
|
||||
paletteData: Float32Array,
|
||||
patternMeta: Float32Array,
|
||||
patternData: Uint8Array,
|
||||
): void {
|
||||
this.updatePalette(paletteData);
|
||||
|
||||
const gl = this.gl;
|
||||
const palW = getPaletteSize();
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.patternMetaTex);
|
||||
gl.texSubImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
palW,
|
||||
1,
|
||||
gl.RGBA,
|
||||
gl.FLOAT,
|
||||
patternMeta,
|
||||
);
|
||||
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.patternDataTex);
|
||||
gl.texSubImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1024,
|
||||
palW,
|
||||
gl.RED_INTEGER,
|
||||
gl.UNSIGNED_BYTE,
|
||||
patternData,
|
||||
);
|
||||
|
||||
this.namePass.addPlayers(players, this.paletteData);
|
||||
for (const p of players) {
|
||||
if (p.team !== null) this.playerTeams.set(p.smallID, p.team);
|
||||
@@ -838,6 +897,10 @@ export class GPURenderer {
|
||||
this.trailPass.setAltView(active);
|
||||
}
|
||||
|
||||
setShowPatterns(active: boolean): void {
|
||||
this.territoryPass.setShowPatterns(active);
|
||||
}
|
||||
|
||||
setGridView(active: boolean): void {
|
||||
this.gridView = active;
|
||||
}
|
||||
@@ -1147,6 +1210,8 @@ export class GPURenderer {
|
||||
this.barPass.dispose();
|
||||
disposeGPUResources(this.gl, this.res);
|
||||
this.gl.deleteTexture(this.paletteTex);
|
||||
this.gl.deleteTexture(this.patternMetaTex);
|
||||
this.gl.deleteTexture(this.patternDataTex);
|
||||
this.gl.deleteFramebuffer(this.sceneTarget.fbo);
|
||||
this.gl.deleteTexture(this.sceneTarget.tex);
|
||||
this.lastUnits = new Map();
|
||||
|
||||
@@ -36,14 +36,18 @@ export class TerritoryPass {
|
||||
private uCharcoalAlpha: WebGLUniformLocation;
|
||||
private uHighlightOwner: WebGLUniformLocation;
|
||||
private uHighlightBrighten: WebGLUniformLocation;
|
||||
private uShowPatterns: WebGLUniformLocation;
|
||||
private highlightOwner = 0;
|
||||
|
||||
private vao: WebGLVertexArrayObject;
|
||||
private tileTex: WebGLTexture;
|
||||
private trailTex: WebGLTexture;
|
||||
private paletteTex: WebGLTexture;
|
||||
private patternMetaTex: WebGLTexture;
|
||||
private patternDataTex: WebGLTexture;
|
||||
|
||||
private altView = false;
|
||||
private showPatterns = true;
|
||||
|
||||
/** CPU-side tile state (deltas written here, flushed to GPU before draw). */
|
||||
private cpuTileState: Uint16Array;
|
||||
@@ -72,6 +76,8 @@ export class TerritoryPass {
|
||||
tileTex: WebGLTexture,
|
||||
trailTex: WebGLTexture,
|
||||
paletteTex: WebGLTexture,
|
||||
patternMetaTex: WebGLTexture,
|
||||
patternDataTex: WebGLTexture,
|
||||
settings: RenderSettings,
|
||||
) {
|
||||
this.gl = gl;
|
||||
@@ -81,6 +87,8 @@ export class TerritoryPass {
|
||||
this.tileTex = tileTex;
|
||||
this.trailTex = trailTex;
|
||||
this.paletteTex = paletteTex;
|
||||
this.patternMetaTex = patternMetaTex;
|
||||
this.patternDataTex = patternDataTex;
|
||||
this.cpuTileState = new Uint16Array(mapW * mapH);
|
||||
this.cpuTrailState = new Uint8Array(mapW * mapH);
|
||||
|
||||
@@ -112,10 +120,13 @@ export class TerritoryPass {
|
||||
this.program,
|
||||
"uHighlightBrighten",
|
||||
)!;
|
||||
this.uShowPatterns = gl.getUniformLocation(this.program, "uShowPatterns")!;
|
||||
|
||||
gl.useProgram(this.program);
|
||||
gl.uniform1i(gl.getUniformLocation(this.program, "uTileTex"), 0);
|
||||
gl.uniform1i(gl.getUniformLocation(this.program, "uPalette"), 1);
|
||||
gl.uniform1i(gl.getUniformLocation(this.program, "uPatternMeta"), 2);
|
||||
gl.uniform1i(gl.getUniformLocation(this.program, "uPatternData"), 3);
|
||||
|
||||
this.vao = createMapQuad(gl, mapW, mapH);
|
||||
}
|
||||
@@ -330,6 +341,10 @@ export class TerritoryPass {
|
||||
this.altView = active;
|
||||
}
|
||||
|
||||
setShowPatterns(show: boolean): void {
|
||||
this.showPatterns = show;
|
||||
}
|
||||
|
||||
/** Set the hovered player's smallID for territory-fill brightening (0 = off). */
|
||||
setHighlightOwner(ownerID: number): void {
|
||||
this.highlightOwner = ownerID;
|
||||
@@ -352,11 +367,19 @@ export class TerritoryPass {
|
||||
gl.uniform1f(this.uCharcoalAlpha, mo.charcoalAlpha);
|
||||
gl.uniform1ui(this.uHighlightOwner, this.highlightOwner);
|
||||
gl.uniform1f(this.uHighlightBrighten, mo.highlightFillBrighten);
|
||||
gl.uniform1i(
|
||||
this.uShowPatterns,
|
||||
this.settings.passEnabled.territoryPatterns && this.showPatterns ? 1 : 0,
|
||||
);
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.tileTex);
|
||||
gl.activeTexture(gl.TEXTURE1);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.paletteTex);
|
||||
gl.activeTexture(gl.TEXTURE2);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.patternMetaTex);
|
||||
gl.activeTexture(gl.TEXTURE3);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.patternDataTex);
|
||||
|
||||
gl.bindVertexArray(this.vao);
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
@@ -366,6 +389,6 @@ export class TerritoryPass {
|
||||
const gl = this.gl;
|
||||
gl.deleteProgram(this.program);
|
||||
gl.deleteVertexArray(this.vao);
|
||||
// tileTex, trailTex, paletteTex owned by GPUResources / renderer
|
||||
// tileTex, trailTex, paletteTex, patternMetaTex, patternDataTex owned by GPUResources / renderer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"passEnabled": {
|
||||
"terrain": true,
|
||||
"mapOverlay": true,
|
||||
"territoryPatterns": true,
|
||||
"structure": true,
|
||||
"unit": true,
|
||||
"name": true,
|
||||
|
||||
@@ -4,6 +4,9 @@ precision highp usampler2D;
|
||||
|
||||
uniform usampler2D uTileTex; // R16UI — tile state per cell
|
||||
uniform sampler2D uPalette; // RGBA32F — player colors
|
||||
uniform sampler2D uPatternMeta; // RGBA32F — 1D buffer, 1 px per owner. R=hasPattern, G=width, B=height, A=scale
|
||||
uniform usampler2D uPatternData; // R8UI — 2D buffer, row per owner, bytes for bitmask
|
||||
uniform int uShowPatterns;
|
||||
|
||||
uniform vec2 uMapSize;
|
||||
uniform int uAltView;
|
||||
@@ -42,6 +45,29 @@ void main() {
|
||||
float u = (float(owner) + 0.5) / float(PALETTE_SIZE);
|
||||
vec4 color = texture(uPalette, vec2(u, 0.25));
|
||||
|
||||
if (uShowPatterns == 1) {
|
||||
vec4 meta = texelFetch(uPatternMeta, ivec2(int(owner), 0), 0);
|
||||
if (meta.r > 0.0) {
|
||||
int pWidth = int(meta.g);
|
||||
int pHeight = int(meta.b);
|
||||
int pScale = int(meta.a);
|
||||
|
||||
int px = tc.x >> pScale;
|
||||
int py = tc.y >> pScale;
|
||||
int mx = ((px % pWidth) + pWidth) % pWidth;
|
||||
int my = ((py % pHeight) + pHeight) % pHeight;
|
||||
int bitIndex = my * pWidth + mx;
|
||||
int byteIndex = bitIndex >> 3;
|
||||
|
||||
uint patternByte = texelFetch(uPatternData, ivec2(byteIndex, int(owner)), 0).r;
|
||||
bool isPrimary = (patternByte & (1u << uint(bitIndex & 7))) == 0u;
|
||||
|
||||
if (!isPrimary) {
|
||||
color = texture(uPalette, vec2(u, 0.75));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hover highlight: brighten every tile owned by the hovered player.
|
||||
if (uHighlightOwner != 0u && owner == uHighlightOwner) {
|
||||
color.rgb = mix(color.rgb, vec3(1.0), uHighlightBrighten);
|
||||
|
||||
Reference in New Issue
Block a user