mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-03-24 16:17:23 +01:00
fix: streamline texture loading and preload logic in WebGL renderer
This commit is contained in:
parent
4515232e93
commit
dc06f3d65c
3 changed files with 130 additions and 147 deletions
106
src/config/relief-config.ts
Normal file
106
src/config/relief-config.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
export const RELIEF_SYMBOLS: Record<string, string[]> = {
|
||||||
|
simple: [
|
||||||
|
"relief-mount-1",
|
||||||
|
"relief-hill-1",
|
||||||
|
"relief-conifer-1",
|
||||||
|
"relief-deciduous-1",
|
||||||
|
"relief-acacia-1",
|
||||||
|
"relief-palm-1",
|
||||||
|
"relief-grass-1",
|
||||||
|
"relief-swamp-1",
|
||||||
|
"relief-dune-1",
|
||||||
|
],
|
||||||
|
gray: [
|
||||||
|
"relief-mount-2-bw",
|
||||||
|
"relief-mount-3-bw",
|
||||||
|
"relief-mount-4-bw",
|
||||||
|
"relief-mount-5-bw",
|
||||||
|
"relief-mount-6-bw",
|
||||||
|
"relief-mount-7-bw",
|
||||||
|
"relief-mountSnow-1-bw",
|
||||||
|
"relief-mountSnow-2-bw",
|
||||||
|
"relief-mountSnow-3-bw",
|
||||||
|
"relief-mountSnow-4-bw",
|
||||||
|
"relief-mountSnow-5-bw",
|
||||||
|
"relief-mountSnow-6-bw",
|
||||||
|
"relief-hill-2-bw",
|
||||||
|
"relief-hill-3-bw",
|
||||||
|
"relief-hill-4-bw",
|
||||||
|
"relief-hill-5-bw",
|
||||||
|
"relief-conifer-2-bw",
|
||||||
|
"relief-coniferSnow-1-bw",
|
||||||
|
"relief-swamp-2-bw",
|
||||||
|
"relief-swamp-3-bw",
|
||||||
|
"relief-cactus-1-bw",
|
||||||
|
"relief-cactus-2-bw",
|
||||||
|
"relief-cactus-3-bw",
|
||||||
|
"relief-deadTree-1-bw",
|
||||||
|
"relief-deadTree-2-bw",
|
||||||
|
"relief-vulcan-1-bw",
|
||||||
|
"relief-vulcan-2-bw",
|
||||||
|
"relief-vulcan-3-bw",
|
||||||
|
"relief-dune-2-bw",
|
||||||
|
"relief-grass-2-bw",
|
||||||
|
"relief-acacia-2-bw",
|
||||||
|
"relief-palm-2-bw",
|
||||||
|
"relief-deciduous-2-bw",
|
||||||
|
"relief-deciduous-3-bw",
|
||||||
|
],
|
||||||
|
colored: [
|
||||||
|
"relief-mount-2",
|
||||||
|
"relief-mount-3",
|
||||||
|
"relief-mount-4",
|
||||||
|
"relief-mount-5",
|
||||||
|
"relief-mount-6",
|
||||||
|
"relief-mount-7",
|
||||||
|
"relief-mountSnow-1",
|
||||||
|
"relief-mountSnow-2",
|
||||||
|
"relief-mountSnow-3",
|
||||||
|
"relief-mountSnow-4",
|
||||||
|
"relief-mountSnow-5",
|
||||||
|
"relief-mountSnow-6",
|
||||||
|
"relief-hill-2",
|
||||||
|
"relief-hill-3",
|
||||||
|
"relief-hill-4",
|
||||||
|
"relief-hill-5",
|
||||||
|
"relief-conifer-2",
|
||||||
|
"relief-coniferSnow-1",
|
||||||
|
"relief-swamp-2",
|
||||||
|
"relief-swamp-3",
|
||||||
|
"relief-cactus-1",
|
||||||
|
"relief-cactus-2",
|
||||||
|
"relief-cactus-3",
|
||||||
|
"relief-deadTree-1",
|
||||||
|
"relief-deadTree-2",
|
||||||
|
"relief-vulcan-1",
|
||||||
|
"relief-vulcan-2",
|
||||||
|
"relief-vulcan-3",
|
||||||
|
"relief-dune-2",
|
||||||
|
"relief-grass-2",
|
||||||
|
"relief-acacia-2",
|
||||||
|
"relief-palm-2",
|
||||||
|
"relief-deciduous-2",
|
||||||
|
"relief-deciduous-3",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const VARIANT_RANGES: Record<string, [number, number]> = {
|
||||||
|
mount: [2, 7],
|
||||||
|
mountSnow: [1, 6],
|
||||||
|
hill: [2, 5],
|
||||||
|
conifer: [2, 2],
|
||||||
|
coniferSnow: [1, 1],
|
||||||
|
swamp: [2, 3],
|
||||||
|
cactus: [1, 3],
|
||||||
|
deadTree: [1, 2],
|
||||||
|
vulcan: [1, 3],
|
||||||
|
deciduous: [2, 3],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const COLORED_TO_SIMPLE_MAP: Record<string, string> = {
|
||||||
|
mountSnow: "mount",
|
||||||
|
vulcan: "mount",
|
||||||
|
coniferSnow: "conifer",
|
||||||
|
cactus: "dune",
|
||||||
|
deadTree: "dune",
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { extent, polygonContains } from "d3";
|
import { extent, polygonContains } from "d3";
|
||||||
|
import { COLORED_TO_SIMPLE_MAP, VARIANT_RANGES } from "../config/relief-config";
|
||||||
import {
|
import {
|
||||||
byId,
|
byId,
|
||||||
getPackPolygon,
|
getPackPolygon,
|
||||||
|
|
@ -118,127 +119,6 @@ export function generateRelief(): ReliefIcon[] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Utilities ─────────────────────────────────────────────────────────
|
|
||||||
export const RELIEF_SYMBOLS: Record<string, string[]> = {
|
|
||||||
simple: [
|
|
||||||
"relief-mount-1",
|
|
||||||
"relief-hill-1",
|
|
||||||
"relief-conifer-1",
|
|
||||||
"relief-deciduous-1",
|
|
||||||
"relief-acacia-1",
|
|
||||||
"relief-palm-1",
|
|
||||||
"relief-grass-1",
|
|
||||||
"relief-swamp-1",
|
|
||||||
"relief-dune-1",
|
|
||||||
],
|
|
||||||
gray: [
|
|
||||||
"relief-mount-2-bw",
|
|
||||||
"relief-mount-3-bw",
|
|
||||||
"relief-mount-4-bw",
|
|
||||||
"relief-mount-5-bw",
|
|
||||||
"relief-mount-6-bw",
|
|
||||||
"relief-mount-7-bw",
|
|
||||||
"relief-mountSnow-1-bw",
|
|
||||||
"relief-mountSnow-2-bw",
|
|
||||||
"relief-mountSnow-3-bw",
|
|
||||||
"relief-mountSnow-4-bw",
|
|
||||||
"relief-mountSnow-5-bw",
|
|
||||||
"relief-mountSnow-6-bw",
|
|
||||||
"relief-hill-2-bw",
|
|
||||||
"relief-hill-3-bw",
|
|
||||||
"relief-hill-4-bw",
|
|
||||||
"relief-hill-5-bw",
|
|
||||||
"relief-conifer-2-bw",
|
|
||||||
"relief-coniferSnow-1-bw",
|
|
||||||
"relief-swamp-2-bw",
|
|
||||||
"relief-swamp-3-bw",
|
|
||||||
"relief-cactus-1-bw",
|
|
||||||
"relief-cactus-2-bw",
|
|
||||||
"relief-cactus-3-bw",
|
|
||||||
"relief-deadTree-1-bw",
|
|
||||||
"relief-deadTree-2-bw",
|
|
||||||
"relief-vulcan-1-bw",
|
|
||||||
"relief-vulcan-2-bw",
|
|
||||||
"relief-vulcan-3-bw",
|
|
||||||
"relief-dune-2-bw",
|
|
||||||
"relief-grass-2-bw",
|
|
||||||
"relief-acacia-2-bw",
|
|
||||||
"relief-palm-2-bw",
|
|
||||||
"relief-deciduous-2-bw",
|
|
||||||
"relief-deciduous-3-bw",
|
|
||||||
],
|
|
||||||
colored: [
|
|
||||||
"relief-mount-2",
|
|
||||||
"relief-mount-3",
|
|
||||||
"relief-mount-4",
|
|
||||||
"relief-mount-5",
|
|
||||||
"relief-mount-6",
|
|
||||||
"relief-mount-7",
|
|
||||||
"relief-mountSnow-1",
|
|
||||||
"relief-mountSnow-2",
|
|
||||||
"relief-mountSnow-3",
|
|
||||||
"relief-mountSnow-4",
|
|
||||||
"relief-mountSnow-5",
|
|
||||||
"relief-mountSnow-6",
|
|
||||||
"relief-hill-2",
|
|
||||||
"relief-hill-3",
|
|
||||||
"relief-hill-4",
|
|
||||||
"relief-hill-5",
|
|
||||||
"relief-conifer-2",
|
|
||||||
"relief-coniferSnow-1",
|
|
||||||
"relief-swamp-2",
|
|
||||||
"relief-swamp-3",
|
|
||||||
"relief-cactus-1",
|
|
||||||
"relief-cactus-2",
|
|
||||||
"relief-cactus-3",
|
|
||||||
"relief-deadTree-1",
|
|
||||||
"relief-deadTree-2",
|
|
||||||
"relief-vulcan-1",
|
|
||||||
"relief-vulcan-2",
|
|
||||||
"relief-vulcan-3",
|
|
||||||
"relief-dune-2",
|
|
||||||
"relief-grass-2",
|
|
||||||
"relief-acacia-2",
|
|
||||||
"relief-palm-2",
|
|
||||||
"relief-deciduous-2",
|
|
||||||
"relief-deciduous-3",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// map a symbol href to its atlas set and tile index
|
|
||||||
export function resolveSprite(symbolHref: string): {
|
|
||||||
set: string;
|
|
||||||
tileIndex: number;
|
|
||||||
} {
|
|
||||||
const id = symbolHref.startsWith("#") ? symbolHref.slice(1) : symbolHref;
|
|
||||||
for (const [set, ids] of Object.entries(RELIEF_SYMBOLS)) {
|
|
||||||
const idx = ids.indexOf(id);
|
|
||||||
if (idx !== -1) return { set, tileIndex: idx };
|
|
||||||
}
|
|
||||||
throw new Error(`Relief: unknown symbol href "${symbolHref}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const VARIANT_RANGES: Record<string, [number, number]> = {
|
|
||||||
mount: [2, 7],
|
|
||||||
mountSnow: [1, 6],
|
|
||||||
hill: [2, 5],
|
|
||||||
conifer: [2, 2],
|
|
||||||
coniferSnow: [1, 1],
|
|
||||||
swamp: [2, 3],
|
|
||||||
cactus: [1, 3],
|
|
||||||
deadTree: [1, 2],
|
|
||||||
vulcan: [1, 3],
|
|
||||||
deciduous: [2, 3],
|
|
||||||
};
|
|
||||||
|
|
||||||
const COLORED_TO_SIMPLE_MAP: Record<string, string> = {
|
|
||||||
mountSnow: "mount",
|
|
||||||
vulcan: "mount",
|
|
||||||
coniferSnow: "conifer",
|
|
||||||
cactus: "dune",
|
|
||||||
deadTree: "dune",
|
|
||||||
};
|
|
||||||
|
|
||||||
function getVariant(type: string): number {
|
function getVariant(type: string): number {
|
||||||
const range = VARIANT_RANGES[type];
|
const range = VARIANT_RANGES[type];
|
||||||
return range ? rand(...range) : 2;
|
return range ? rand(...range) : 2;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
|
import { RELIEF_SYMBOLS } from "../config/relief-config";
|
||||||
import type { ReliefIcon } from "../modules/relief-generator";
|
import type { ReliefIcon } from "../modules/relief-generator";
|
||||||
import {
|
import { generateRelief } from "../modules/relief-generator";
|
||||||
generateRelief,
|
|
||||||
RELIEF_SYMBOLS,
|
|
||||||
resolveSprite,
|
|
||||||
} from "../modules/relief-generator";
|
|
||||||
import { byId } from "../utils";
|
import { byId } from "../utils";
|
||||||
|
|
||||||
// ── Module state ───────────────────────────────────────────────────────────────
|
|
||||||
let fo: SVGForeignObjectElement | null = null;
|
let fo: SVGForeignObjectElement | null = null;
|
||||||
let renderer: any = null; // THREE.WebGLRenderer
|
let renderer: any = null; // THREE.WebGLRenderer
|
||||||
let camera: any = null; // THREE.OrthographicCamera
|
let camera: any = null; // THREE.OrthographicCamera
|
||||||
|
|
@ -15,7 +11,9 @@ let scene: any = null; // THREE.Scene
|
||||||
|
|
||||||
const textureCache = new Map<string, any>(); // set name → THREE.Texture
|
const textureCache = new Map<string, any>(); // set name → THREE.Texture
|
||||||
|
|
||||||
// ── Texture ────────────────────────────────────────────────────────────────────
|
function preloadTextures(): void {
|
||||||
|
for (const set of Object.keys(RELIEF_SYMBOLS)) loadTexture(set);
|
||||||
|
}
|
||||||
|
|
||||||
function loadTexture(set: string): Promise<any> {
|
function loadTexture(set: string): Promise<any> {
|
||||||
if (textureCache.has(set)) return Promise.resolve(textureCache.get(set));
|
if (textureCache.has(set)) return Promise.resolve(textureCache.get(set));
|
||||||
|
|
@ -30,7 +28,8 @@ function loadTexture(set: string): Promise<any> {
|
||||||
texture.minFilter = THREE.LinearMipmapLinearFilter;
|
texture.minFilter = THREE.LinearMipmapLinearFilter;
|
||||||
texture.magFilter = THREE.LinearFilter;
|
texture.magFilter = THREE.LinearFilter;
|
||||||
texture.generateMipmaps = true;
|
texture.generateMipmaps = true;
|
||||||
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
|
if (renderer)
|
||||||
|
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
|
||||||
textureCache.set(set, texture);
|
textureCache.set(set, texture);
|
||||||
resolve(texture);
|
resolve(texture);
|
||||||
},
|
},
|
||||||
|
|
@ -43,8 +42,6 @@ function loadTexture(set: string): Promise<any> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── WebGL bootstrap ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function ensureRenderer(): boolean {
|
function ensureRenderer(): boolean {
|
||||||
const terrainEl = byId("terrain");
|
const terrainEl = byId("terrain");
|
||||||
if (!terrainEl) return false;
|
if (!terrainEl) return false;
|
||||||
|
|
@ -58,7 +55,6 @@ function ensureRenderer(): boolean {
|
||||||
camera = null;
|
camera = null;
|
||||||
scene = null;
|
scene = null;
|
||||||
disposeTextureCache();
|
disposeTextureCache();
|
||||||
// fall through to recreate
|
|
||||||
} else {
|
} else {
|
||||||
if (fo && !fo.isConnected) terrainEl.appendChild(fo);
|
if (fo && !fo.isConnected) terrainEl.appendChild(fo);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -66,10 +62,7 @@ function ensureRenderer(): boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
// foreignObject hosts the WebGL canvas inside the SVG.
|
// foreignObject hosts the WebGL canvas inside the SVG.
|
||||||
fo = document.createElementNS(
|
fo = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
||||||
"http://www.w3.org/2000/svg",
|
|
||||||
"foreignObject",
|
|
||||||
) as unknown as SVGForeignObjectElement;
|
|
||||||
fo.id = "terrainFo";
|
fo.id = "terrainFo";
|
||||||
fo.setAttribute("x", "0");
|
fo.setAttribute("x", "0");
|
||||||
fo.setAttribute("y", "0");
|
fo.setAttribute("y", "0");
|
||||||
|
|
@ -102,19 +95,25 @@ function ensureRenderer(): boolean {
|
||||||
// Camera in SVG coordinate space: top=0, bottom=H puts map y=0 at screen-top.
|
// Camera in SVG coordinate space: top=0, bottom=H puts map y=0 at screen-top.
|
||||||
camera = new THREE.OrthographicCamera(0, graphWidth, 0, graphHeight, -1, 1);
|
camera = new THREE.OrthographicCamera(0, graphWidth, 0, graphHeight, -1, 1);
|
||||||
scene = new THREE.Scene();
|
scene = new THREE.Scene();
|
||||||
|
|
||||||
|
preloadTextures();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Scene / geometry ───────────────────────────────────────────────────────────
|
// map a symbol href to its atlas set and tile index
|
||||||
|
function resolveSprite(symbolHref: string): {
|
||||||
|
set: string;
|
||||||
|
tileIndex: number;
|
||||||
|
} {
|
||||||
|
const id = symbolHref.startsWith("#") ? symbolHref.slice(1) : symbolHref;
|
||||||
|
for (const [set, ids] of Object.entries(RELIEF_SYMBOLS)) {
|
||||||
|
const idx = ids.indexOf(id);
|
||||||
|
if (idx !== -1) return { set, tileIndex: idx };
|
||||||
|
}
|
||||||
|
throw new Error(`Relief: unknown symbol href "${symbolHref}"`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// Build a BufferGeometry with all icon quads for one atlas set.
|
||||||
* Build a BufferGeometry with all icon quads for one atlas set.
|
|
||||||
* Geometry is painter's-order sorted so depth is correct without depth testing.
|
|
||||||
*
|
|
||||||
* UV layout (texture.flipY = false — v=0 is top of image):
|
|
||||||
* u = col/cols … (col+1)/cols
|
|
||||||
* v = row/rows … (row+1)/rows
|
|
||||||
*/
|
|
||||||
function buildSetMesh(icons: ReliefIcon[], set: string, texture: any): any {
|
function buildSetMesh(icons: ReliefIcon[], set: string, texture: any): any {
|
||||||
const ids = RELIEF_SYMBOLS[set] ?? [];
|
const ids = RELIEF_SYMBOLS[set] ?? [];
|
||||||
const n = ids.length || 1;
|
const n = ids.length || 1;
|
||||||
|
|
@ -233,8 +232,6 @@ function renderFrame(): void {
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Private draw / clear ───────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
function drawWebGl(icons: ReliefIcon[]): void {
|
function drawWebGl(icons: ReliefIcon[]): void {
|
||||||
const terrainEl = byId("terrain");
|
const terrainEl = byId("terrain");
|
||||||
if (!terrainEl) return;
|
if (!terrainEl) return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue