mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-27 03:04:39 +00:00
Add terrain colors settings (#4391)
## Description: Add terrain color settings for all terrain types <img width="977" height="485" alt="image" src="https://github.com/user-attachments/assets/ac1cef11-4b1a-45f2-8cf6-94f557ba8f6e" /> ## 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 - [ ] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: MR. Box
This commit is contained in:
@@ -545,6 +545,8 @@
|
||||
"coordinate_grid_opacity_label": "Coordinate grid opacity",
|
||||
"fallout_desc": "Show the green nuclear fallout glow on irradiated territory. Disable to improve performance",
|
||||
"fallout_label": "Fallout effects",
|
||||
"highland_color_desc": "Base color for higher-elevation terrain.",
|
||||
"highland_color_label": "Highland color",
|
||||
"highlight_brighten_desc": "How strongly the border brightens on hover (0 to disable)",
|
||||
"highlight_brighten_label": "Border highlight amount",
|
||||
"highlight_fill_desc": "How strongly territory brightens on hover (0 to disable)",
|
||||
@@ -563,6 +565,8 @@
|
||||
"lighting_ambient_label": "Ambient light",
|
||||
"lighting_unit_glow_desc": "How far the glow spreads around units and structures",
|
||||
"lighting_unit_glow_label": "Unit glow",
|
||||
"mountain_color_desc": "Base color for mountain terrain.",
|
||||
"mountain_color_label": "Mountain color",
|
||||
"name_cull_desc": "Hide names smaller than this size",
|
||||
"name_cull_label": "Minimum name size",
|
||||
"name_scale_label": "Name Scale",
|
||||
@@ -570,12 +574,16 @@
|
||||
"nuke_color_label": "Nuke fallout color",
|
||||
"ocean_color_desc": "Base color of ocean.",
|
||||
"ocean_color_label": "Ocean color",
|
||||
"plains_color_desc": "Base color for lowland terrain.",
|
||||
"plains_color_label": "Plains color",
|
||||
"rail_distance_desc": "How far zoomed out train tracks remain visible",
|
||||
"rail_distance_label": "Train track draw distance",
|
||||
"rail_thickness_desc": "How wide train tracks are drawn",
|
||||
"rail_thickness_label": "Train track thickness",
|
||||
"reset_desc": "Clear all graphics overrides",
|
||||
"reset_label": "Reset to defaults",
|
||||
"sand_color_desc": "Base color for shores.",
|
||||
"sand_color_label": "Shore color",
|
||||
"section_accessibility": "Accessibility",
|
||||
"section_effects": "Effects",
|
||||
"section_lighting": "Lighting",
|
||||
|
||||
@@ -425,6 +425,34 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
|
||||
);
|
||||
}
|
||||
|
||||
private currentSandColor(): string {
|
||||
return (
|
||||
this.userSettings.graphicsOverrides().terrain?.sandColor ??
|
||||
renderDefaults.terrain.sandColor
|
||||
);
|
||||
}
|
||||
|
||||
private currentPlainsColor(): string {
|
||||
return (
|
||||
this.userSettings.graphicsOverrides().terrain?.plainsColor ??
|
||||
renderDefaults.terrain.plainsColor
|
||||
);
|
||||
}
|
||||
|
||||
private currentHighlandColor(): string {
|
||||
return (
|
||||
this.userSettings.graphicsOverrides().terrain?.highlandColor ??
|
||||
renderDefaults.terrain.highlandColor
|
||||
);
|
||||
}
|
||||
|
||||
private currentMountainColor(): string {
|
||||
return (
|
||||
this.userSettings.graphicsOverrides().terrain?.mountainColor ??
|
||||
renderDefaults.terrain.mountainColor
|
||||
);
|
||||
}
|
||||
|
||||
private onOceanColorChange(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value.trim();
|
||||
const match = HEX_COLOR_RE.exec(value);
|
||||
@@ -432,6 +460,34 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
|
||||
this.patchTerrain({ oceanColor: `#${match[1].toLowerCase()}` });
|
||||
}
|
||||
|
||||
private onSandColorChange(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value.trim();
|
||||
const match = HEX_COLOR_RE.exec(value);
|
||||
if (!match) return; // ignore partial/invalid hex while typing
|
||||
this.patchTerrain({ sandColor: `#${match[1].toLowerCase()}` });
|
||||
}
|
||||
|
||||
private onPlainsColorChange(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value.trim();
|
||||
const match = HEX_COLOR_RE.exec(value);
|
||||
if (!match) return;
|
||||
this.patchTerrain({ plainsColor: `#${match[1].toLowerCase()}` });
|
||||
}
|
||||
|
||||
private onHighlandColorChange(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value.trim();
|
||||
const match = HEX_COLOR_RE.exec(value);
|
||||
if (!match) return;
|
||||
this.patchTerrain({ highlandColor: `#${match[1].toLowerCase()}` });
|
||||
}
|
||||
|
||||
private onMountainColorChange(event: Event) {
|
||||
const value = (event.target as HTMLInputElement).value.trim();
|
||||
const match = HEX_COLOR_RE.exec(value);
|
||||
if (!match) return;
|
||||
this.patchTerrain({ mountainColor: `#${match[1].toLowerCase()}` });
|
||||
}
|
||||
|
||||
private patchLighting(patch: Partial<GraphicsOverrides["lighting"]>) {
|
||||
const current = this.userSettings.graphicsOverrides();
|
||||
this.userSettings.setGraphicsOverrides({
|
||||
@@ -611,6 +667,10 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
|
||||
const railDrawDistance = RAIL_ZOOM_MAX - this.currentRailMinZoom();
|
||||
const railThickness = this.currentRailThickness();
|
||||
const oceanColor = this.currentOceanColor();
|
||||
const sandColor = this.currentSandColor();
|
||||
const plainsColor = this.currentPlainsColor();
|
||||
const highlandColor = this.currentHighlandColor();
|
||||
const mountainColor = this.currentMountainColor();
|
||||
const nukeColor = this.currentNukeColor();
|
||||
const ambientLevel = this.currentAmbientLevel();
|
||||
const unitGlow = this.currentUnitGlow();
|
||||
@@ -1182,6 +1242,114 @@ export class GraphicsSettingsModal extends LitElement implements Controller {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">
|
||||
${translateText("graphics_setting.sand_color_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${translateText("graphics_setting.sand_color_desc")}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
.value=${sandColor}
|
||||
placeholder=${renderDefaults.terrain.sandColor}
|
||||
spellcheck="false"
|
||||
@change=${this.onSandColorChange}
|
||||
class="w-24 px-2 py-1 bg-slate-900 border border-slate-500 rounded-sm text-sm text-white font-mono"
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
.value=${sandColor}
|
||||
@input=${this.onSandColorChange}
|
||||
class="w-10 h-8 bg-transparent border border-slate-500 rounded-sm cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">
|
||||
${translateText("graphics_setting.plains_color_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${translateText("graphics_setting.plains_color_desc")}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
.value=${plainsColor}
|
||||
placeholder=${renderDefaults.terrain.plainsColor}
|
||||
spellcheck="false"
|
||||
@change=${this.onPlainsColorChange}
|
||||
class="w-24 px-2 py-1 bg-slate-900 border border-slate-500 rounded-sm text-sm text-white font-mono"
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
.value=${plainsColor}
|
||||
@input=${this.onPlainsColorChange}
|
||||
class="w-10 h-8 bg-transparent border border-slate-500 rounded-sm cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">
|
||||
${translateText("graphics_setting.highland_color_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${translateText("graphics_setting.highland_color_desc")}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
.value=${highlandColor}
|
||||
placeholder=${renderDefaults.terrain.highlandColor}
|
||||
spellcheck="false"
|
||||
@change=${this.onHighlandColorChange}
|
||||
class="w-24 px-2 py-1 bg-slate-900 border border-slate-500 rounded-sm text-sm text-white font-mono"
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
.value=${highlandColor}
|
||||
@input=${this.onHighlandColorChange}
|
||||
class="w-10 h-8 bg-transparent border border-slate-500 rounded-sm cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium">
|
||||
${translateText("graphics_setting.mountain_color_label")}
|
||||
</div>
|
||||
<div class="text-sm text-slate-400">
|
||||
${translateText("graphics_setting.mountain_color_desc")}
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
.value=${mountainColor}
|
||||
placeholder=${renderDefaults.terrain.mountainColor}
|
||||
spellcheck="false"
|
||||
@change=${this.onMountainColorChange}
|
||||
class="w-24 px-2 py-1 bg-slate-900 border border-slate-500 rounded-sm text-sm text-white font-mono"
|
||||
/>
|
||||
<input
|
||||
type="color"
|
||||
.value=${mountainColor}
|
||||
@input=${this.onMountainColorChange}
|
||||
class="w-10 h-8 bg-transparent border border-slate-500 rounded-sm cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex gap-3 items-center w-full text-left p-3 hover:bg-slate-700 rounded-sm text-white transition-colors"
|
||||
>
|
||||
|
||||
@@ -58,6 +58,10 @@ export const GraphicsOverridesSchema = z
|
||||
.object({
|
||||
// "#rrggbb" hex string; overrides the base ocean (deep water) color.
|
||||
oceanColor: z.string(),
|
||||
sandColor: z.string(),
|
||||
plainsColor: z.string(),
|
||||
highlandColor: z.string(),
|
||||
mountainColor: z.string(),
|
||||
})
|
||||
.partial(),
|
||||
lighting: z
|
||||
|
||||
@@ -97,6 +97,18 @@ export function applyGraphicsOverrides(
|
||||
if (overrides.terrain?.oceanColor !== undefined) {
|
||||
settings.terrain.oceanColor = overrides.terrain.oceanColor;
|
||||
}
|
||||
if (overrides.terrain?.sandColor !== undefined) {
|
||||
settings.terrain.sandColor = overrides.terrain.sandColor;
|
||||
}
|
||||
if (overrides.terrain?.plainsColor !== undefined) {
|
||||
settings.terrain.plainsColor = overrides.terrain.plainsColor;
|
||||
}
|
||||
if (overrides.terrain?.highlandColor !== undefined) {
|
||||
settings.terrain.highlandColor = overrides.terrain.highlandColor;
|
||||
}
|
||||
if (overrides.terrain?.mountainColor !== undefined) {
|
||||
settings.terrain.mountainColor = overrides.terrain.mountainColor;
|
||||
}
|
||||
if (overrides.lighting?.ambient !== undefined) {
|
||||
settings.lighting.ambient = overrides.lighting.ambient;
|
||||
// The composite only darkens the scene (and reveals the structure/unit
|
||||
|
||||
@@ -64,6 +64,10 @@ export interface RenderSettings {
|
||||
* per-depth brightness gradient is preserved relative to this color.
|
||||
*/
|
||||
oceanColor: string;
|
||||
sandColor: string;
|
||||
plainsColor: string;
|
||||
highlandColor: string;
|
||||
mountainColor: string;
|
||||
};
|
||||
falloutBloom: {
|
||||
broilSpeedCold: number;
|
||||
|
||||
@@ -214,13 +214,13 @@ export class GPURenderer {
|
||||
this.camera = new Camera(mapW, mapH);
|
||||
|
||||
// --- Terrain (static) ---
|
||||
this.terrainPass = new TerrainPass(
|
||||
gl,
|
||||
terrainBytes,
|
||||
mapW,
|
||||
mapH,
|
||||
hexToRgb(this.settings.terrain.oceanColor) ?? undefined,
|
||||
);
|
||||
this.terrainPass = new TerrainPass(gl, terrainBytes, mapW, mapH, {
|
||||
oceanColor: hexToRgb(this.settings.terrain.oceanColor) ?? undefined,
|
||||
sandColor: hexToRgb(this.settings.terrain.sandColor) ?? undefined,
|
||||
plainsColor: hexToRgb(this.settings.terrain.plainsColor) ?? undefined,
|
||||
highlandColor: hexToRgb(this.settings.terrain.highlandColor) ?? undefined,
|
||||
mountainColor: hexToRgb(this.settings.terrain.mountainColor) ?? undefined,
|
||||
});
|
||||
|
||||
// --- Shared palette texture (RGBA32F, 4096×2) ---
|
||||
this.paletteData = paletteData;
|
||||
@@ -835,9 +835,13 @@ export class GPURenderer {
|
||||
* settings change needs this explicit rebuild.
|
||||
*/
|
||||
rebuildTerrain(): void {
|
||||
this.terrainPass.setOceanColor(
|
||||
hexToRgb(this.settings.terrain.oceanColor) ?? undefined,
|
||||
);
|
||||
this.terrainPass.setTerrainColors({
|
||||
oceanColor: hexToRgb(this.settings.terrain.oceanColor) ?? undefined,
|
||||
sandColor: hexToRgb(this.settings.terrain.sandColor) ?? undefined,
|
||||
plainsColor: hexToRgb(this.settings.terrain.plainsColor) ?? undefined,
|
||||
highlandColor: hexToRgb(this.settings.terrain.highlandColor) ?? undefined,
|
||||
mountainColor: hexToRgb(this.settings.terrain.mountainColor) ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
applyConquestEvents(events: ConquestFx[]): void {
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
|
||||
import terrainFragSrc from "../shaders/terrain/terrain.frag.glsl?raw";
|
||||
import terrainVertSrc from "../shaders/terrain/terrain.vert.glsl?raw";
|
||||
import { buildTerrainRGBA, encodeTerrainTile } from "../utils/ColorUtils";
|
||||
import {
|
||||
buildTerrainRGBA,
|
||||
encodeTerrainTile,
|
||||
TerrainColorOverrides,
|
||||
} from "../utils/ColorUtils";
|
||||
import {
|
||||
createMapQuad,
|
||||
createProgram,
|
||||
@@ -30,7 +34,7 @@ export class TerrainPass {
|
||||
private mapW: number;
|
||||
private mapH: number;
|
||||
// Base ocean (deep water) color; reused by applyTerrainDelta and rebuilds.
|
||||
private oceanColor: readonly [number, number, number] | undefined;
|
||||
private terrainColors: TerrainColorOverrides | undefined;
|
||||
// Scratch buffer for 1×1 sub-uploads; reused across applyTerrainDelta calls.
|
||||
private readonly pixelScratch = new Uint8Array(4);
|
||||
|
||||
@@ -39,11 +43,11 @@ export class TerrainPass {
|
||||
private terrainBytes: Uint8Array,
|
||||
mapW: number,
|
||||
mapH: number,
|
||||
oceanColor?: readonly [number, number, number],
|
||||
terrainColors?: TerrainColorOverrides,
|
||||
) {
|
||||
this.mapW = mapW;
|
||||
this.mapH = mapH;
|
||||
this.oceanColor = oceanColor;
|
||||
this.terrainColors = terrainColors;
|
||||
this.program = createProgram(
|
||||
gl,
|
||||
shaderSrc(terrainVertSrc, { MAP_W: mapW, MAP_H: mapH }),
|
||||
@@ -57,7 +61,7 @@ export class TerrainPass {
|
||||
internalFormat: gl.RGBA8,
|
||||
format: gl.RGBA,
|
||||
type: gl.UNSIGNED_BYTE,
|
||||
data: buildTerrainRGBA(terrainBytes, mapW, mapH, oceanColor),
|
||||
data: buildTerrainRGBA(terrainBytes, mapW, mapH, terrainColors),
|
||||
filter: gl.NEAREST, // pixel-crisp at all zoom levels
|
||||
});
|
||||
|
||||
@@ -65,11 +69,11 @@ export class TerrainPass {
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the base ocean color and re-upload the whole terrain texture.
|
||||
* Called when the user changes the ocean color in graphics settings.
|
||||
* Replace the base terrain colors and re-upload the whole terrain texture.
|
||||
* Called when the user changes the terrain colors in graphics settings.
|
||||
*/
|
||||
setOceanColor(oceanColor?: readonly [number, number, number]): void {
|
||||
this.oceanColor = oceanColor;
|
||||
setTerrainColors(terrainColors?: TerrainColorOverrides): void {
|
||||
this.terrainColors = terrainColors;
|
||||
const gl = this.gl;
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.tex);
|
||||
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
||||
@@ -82,7 +86,7 @@ export class TerrainPass {
|
||||
this.mapH,
|
||||
gl.RGBA,
|
||||
gl.UNSIGNED_BYTE,
|
||||
buildTerrainRGBA(this.terrainBytes, this.mapW, this.mapH, oceanColor),
|
||||
buildTerrainRGBA(this.terrainBytes, this.mapW, this.mapH, terrainColors),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -93,7 +97,7 @@ export class TerrainPass {
|
||||
* produces.
|
||||
*
|
||||
* Also writes back into `terrainBytes` so a later full re-upload (e.g.
|
||||
* setOceanColor) reflects these conversions instead of reverting them.
|
||||
* setTerrainColor) reflects these conversions instead of reverting them.
|
||||
*/
|
||||
applyTerrainDelta(refs: readonly number[], bytes: Uint8Array): void {
|
||||
if (refs.length === 0) return;
|
||||
@@ -105,7 +109,7 @@ export class TerrainPass {
|
||||
const x = ref % this.mapW;
|
||||
const y = (ref - x) / this.mapW;
|
||||
this.terrainBytes[ref] = bytes[i];
|
||||
encodeTerrainTile(bytes[i], this.pixelScratch, 0, this.oceanColor);
|
||||
encodeTerrainTile(bytes[i], this.pixelScratch, 0, this.terrainColors);
|
||||
gl.texSubImage2D(
|
||||
gl.TEXTURE_2D,
|
||||
0,
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
"nameDebug": false
|
||||
},
|
||||
"terrain": {
|
||||
"oceanColor": "#4785b5"
|
||||
"oceanColor": "#4785b5",
|
||||
"sandColor": "#CC9E9E",
|
||||
"plainsColor": "#BECD8A",
|
||||
"highlandColor": "#C8B78A",
|
||||
"mountainColor": "#e6e6e6"
|
||||
},
|
||||
"falloutBloom": {
|
||||
"broilSpeedCold": 0.0018,
|
||||
|
||||
@@ -52,47 +52,73 @@ const DEEP_WATER_BASE: readonly [number, number, number] = hexToRgb(
|
||||
* indistinguishable from the area outside the map.
|
||||
*/
|
||||
/** Encode one terrain byte → RGBA, writing into `out[offset..offset+3]`. */
|
||||
export interface TerrainColorOverrides {
|
||||
oceanColor?: readonly [number, number, number];
|
||||
sandColor?: readonly [number, number, number];
|
||||
plainsColor?: readonly [number, number, number];
|
||||
highlandColor?: readonly [number, number, number];
|
||||
mountainColor?: readonly [number, number, number];
|
||||
}
|
||||
|
||||
export function encodeTerrainTile(
|
||||
tb: number,
|
||||
out: Uint8Array,
|
||||
offset: number,
|
||||
oceanColor?: readonly [number, number, number],
|
||||
colors?: TerrainColorOverrides,
|
||||
): void {
|
||||
const oceanColor = colors?.oceanColor;
|
||||
const sandColor = colors?.sandColor;
|
||||
const plainsColor = colors?.plainsColor;
|
||||
const highlandColor = colors?.highlandColor;
|
||||
const mountainColor = colors?.mountainColor;
|
||||
|
||||
const isLand = (tb & 0x80) !== 0;
|
||||
const isShoreline = (tb & 0x40) !== 0;
|
||||
const magnitude = tb & 0x1f;
|
||||
|
||||
let r: number, g: number, b: number;
|
||||
|
||||
const terrainColors = {
|
||||
ocean: oceanColor ?? DEEP_WATER_BASE,
|
||||
shoreWater: [100, 143, 255],
|
||||
sand: sandColor ?? [204, 203, 158],
|
||||
plains: plainsColor ?? [190, 220, 138],
|
||||
highland: highlandColor ?? [200, 183, 138],
|
||||
mountain: mountainColor ?? [230, 230, 230],
|
||||
peak: [60, 60, 60],
|
||||
};
|
||||
|
||||
// Impassable terrain: render as the map background colour so it blends
|
||||
// with the area outside the map quad. Must match the clear colour in
|
||||
// Renderer.ts drawBaseLayer(): gl.clearColor(60/255, 60/255, 60/255).
|
||||
if (isLand && magnitude === 31) {
|
||||
r = 60;
|
||||
g = 60;
|
||||
b = 60;
|
||||
[r, g, b] = terrainColors.peak;
|
||||
} else if (isLand && isShoreline) {
|
||||
// Shore (sand)
|
||||
r = 204;
|
||||
g = 203;
|
||||
b = 158;
|
||||
[r, g, b] = terrainColors.sand;
|
||||
} else if (isLand) {
|
||||
if (magnitude < 10) {
|
||||
// Plains
|
||||
r = 190;
|
||||
g = 220 - 2 * magnitude;
|
||||
b = 138;
|
||||
const base = terrainColors.plains;
|
||||
|
||||
r = base[0];
|
||||
g = base[1] - 2 * magnitude;
|
||||
b = base[2];
|
||||
} else if (magnitude < 20) {
|
||||
// Highland
|
||||
r = 200 + 2 * magnitude;
|
||||
g = 183 + 2 * magnitude;
|
||||
b = 138 + 2 * magnitude;
|
||||
const base = terrainColors.highland;
|
||||
const m = magnitude - 10;
|
||||
|
||||
r = Math.min(255, base[0] + 2 * m);
|
||||
g = Math.min(255, base[1] + 2 * m);
|
||||
b = Math.min(255, base[2] + 2 * m);
|
||||
} else {
|
||||
// Mountain
|
||||
const v = Math.min(255, 230 + Math.floor(magnitude / 2));
|
||||
r = v;
|
||||
g = v;
|
||||
b = v;
|
||||
const base = terrainColors.mountain;
|
||||
const m = Math.floor(magnitude / 2);
|
||||
|
||||
r = Math.min(255, base[0] + m);
|
||||
g = Math.min(255, base[1] + m);
|
||||
b = Math.min(255, base[2] + m);
|
||||
}
|
||||
} else if (isShoreline) {
|
||||
// Shoreline water — computed dynamically by blending 70% ocean color and 30% white
|
||||
@@ -105,7 +131,7 @@ export function encodeTerrainTile(
|
||||
// shallowest (brightest) shade; the per-depth gradient is preserved by
|
||||
// subtracting the depth from each channel.
|
||||
const m = Math.min(magnitude, 10);
|
||||
const base = oceanColor ?? DEEP_WATER_BASE;
|
||||
const base = terrainColors.ocean;
|
||||
r = Math.max(0, base[0] - m);
|
||||
g = Math.max(0, base[1] - m);
|
||||
b = Math.max(0, base[2] - m);
|
||||
@@ -121,11 +147,11 @@ export function buildTerrainRGBA(
|
||||
terrainBytes: Uint8Array,
|
||||
w: number,
|
||||
h: number,
|
||||
oceanColor?: readonly [number, number, number],
|
||||
colors?: TerrainColorOverrides,
|
||||
): Uint8Array {
|
||||
const pixels = new Uint8Array(w * h * 4);
|
||||
for (let i = 0; i < w * h; i++) {
|
||||
encodeTerrainTile(terrainBytes[i], pixels, i * 4, oceanColor);
|
||||
encodeTerrainTile(terrainBytes[i], pixels, i * 4, colors);
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user