mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-03-22 23:27:23 +01:00
refactor: Update relief icon handling and streamline texture atlas integration
This commit is contained in:
parent
b17b417d1b
commit
256f36015b
5 changed files with 46 additions and 58 deletions
|
|
@ -1122,10 +1122,11 @@ export function resolveVersionConflicts(mapVersion) {
|
|||
terrainEl.querySelectorAll("use").forEach(u => {
|
||||
const href = u.getAttribute("href") || u.getAttribute("xlink:href") || "";
|
||||
if (!href) return;
|
||||
const icon = href.replace("#", "");
|
||||
const x = +u.getAttribute("x");
|
||||
const y = +u.getAttribute("y");
|
||||
const s = +u.getAttribute("width");
|
||||
relief.push({i: relief.length, href, x, y, s});
|
||||
relief.push({i: relief.length, icon, x, y, s});
|
||||
});
|
||||
terrainEl.innerHTML = "";
|
||||
pack.relief = relief;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
function createAtlas(ids: string[]) {
|
||||
function createAtlas(set: string, ids: string[]) {
|
||||
const url = `images/relief/${set}.png`;
|
||||
const n = ids.length || 1;
|
||||
const cols = Math.ceil(Math.sqrt(n));
|
||||
return { ids, cols, rows: Math.ceil(n / cols) };
|
||||
return { url, ids, cols, rows: Math.ceil(n / cols) };
|
||||
}
|
||||
|
||||
export const RELIEF_SYMBOLS = {
|
||||
simple: createAtlas([
|
||||
export const RELIEF_ATLASES = {
|
||||
simple: createAtlas("simple", [
|
||||
"relief-mount-1",
|
||||
"relief-hill-1",
|
||||
"relief-conifer-1",
|
||||
|
|
@ -16,7 +17,7 @@ export const RELIEF_SYMBOLS = {
|
|||
"relief-swamp-1",
|
||||
"relief-dune-1",
|
||||
]),
|
||||
gray: createAtlas([
|
||||
gray: createAtlas("gray", [
|
||||
"relief-mount-2-bw",
|
||||
"relief-mount-3-bw",
|
||||
"relief-mount-4-bw",
|
||||
|
|
@ -52,7 +53,7 @@ export const RELIEF_SYMBOLS = {
|
|||
"relief-deciduous-2-bw",
|
||||
"relief-deciduous-3-bw",
|
||||
]),
|
||||
colored: createAtlas([
|
||||
colored: createAtlas("colored", [
|
||||
"relief-mount-2",
|
||||
"relief-mount-3",
|
||||
"relief-mount-4",
|
||||
|
|
@ -90,13 +91,6 @@ export const RELIEF_SYMBOLS = {
|
|||
]),
|
||||
};
|
||||
|
||||
export const RELIEF_ATLASES = Object.fromEntries(
|
||||
Object.entries(RELIEF_SYMBOLS).map(([set, { cols, rows }]) => [
|
||||
set,
|
||||
{ url: `images/relief/${set}.png`, cols, rows },
|
||||
]),
|
||||
);
|
||||
|
||||
export const VARIANT_RANGES: Record<string, [number, number]> = {
|
||||
mount: [2, 7],
|
||||
mountSnow: [1, 6],
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import {
|
|||
|
||||
export interface ReliefIcon {
|
||||
i: number;
|
||||
href: string; // e.g. "#relief-mount-1"
|
||||
icon: string; // e.g. "relief-mount-1"
|
||||
x: number;
|
||||
y: number;
|
||||
s: number; // size (width = height in map units)
|
||||
|
|
@ -63,10 +63,10 @@ export function generateRelief(): ReliefIcon[] {
|
|||
if (!polygonContains(polygon, [cx, cy])) continue;
|
||||
let h = (4 + Math.random()) * size;
|
||||
const icon = getBiomeIcon(i, biome);
|
||||
if (icon === "#relief-grass-1") h *= 1.2;
|
||||
if (icon === "relief-grass-1") h *= 1.2;
|
||||
reliefIcons.push({
|
||||
i: reliefIcons.length,
|
||||
href: icon,
|
||||
icon,
|
||||
x: rn(cx - h, 2),
|
||||
y: rn(cy - h, 2),
|
||||
s: rn(h * 2, 2),
|
||||
|
|
@ -85,7 +85,7 @@ export function generateRelief(): ReliefIcon[] {
|
|||
if (!polygonContains(polygon, [cx, cy])) continue;
|
||||
reliefIcons.push({
|
||||
i: reliefIcons.length,
|
||||
href: icon,
|
||||
icon,
|
||||
x: rn(cx - h, 2),
|
||||
y: rn(cy - h, 2),
|
||||
s: rn(h * 2, 2),
|
||||
|
|
@ -125,9 +125,9 @@ function getVariant(type: string): number {
|
|||
}
|
||||
|
||||
function getHref(type: string, set: string): string {
|
||||
if (set === "colored") return `#relief-${type}-${getVariant(type)}`;
|
||||
if (set === "gray") return `#relief-${type}-${getVariant(type)}-bw`;
|
||||
return `#relief-${COLORED_TO_SIMPLE_MAP[type] ?? type}-1`;
|
||||
if (set === "colored") return `relief-${type}-${getVariant(type)}`;
|
||||
if (set === "gray") return `relief-${type}-${getVariant(type)}-bw`;
|
||||
return `relief-${COLORED_TO_SIMPLE_MAP[type] ?? type}-1`;
|
||||
}
|
||||
|
||||
window.generateReliefIcons = generateRelief;
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ import {
|
|||
|
||||
export interface AtlasConfig {
|
||||
url: string;
|
||||
ids: string[];
|
||||
cols: number;
|
||||
rows: number;
|
||||
}
|
||||
|
||||
export interface AtlasQuad {
|
||||
atlasId: string;
|
||||
export interface AtlasItem {
|
||||
icon: string;
|
||||
x: number;
|
||||
y: number;
|
||||
s: number;
|
||||
tileIndex: number;
|
||||
}
|
||||
|
||||
export class TextureAtlasLayer {
|
||||
|
|
@ -47,25 +47,30 @@ export class TextureAtlasLayer {
|
|||
});
|
||||
}
|
||||
|
||||
draw(quads: AtlasQuad[]) {
|
||||
draw(items: AtlasItem[]) {
|
||||
if (!this.group) return;
|
||||
this.disposeGroup();
|
||||
|
||||
const byAtlas = new Map<string, AtlasQuad[]>();
|
||||
for (const q of quads) {
|
||||
let arr = byAtlas.get(q.atlasId);
|
||||
const byAtlas = new Map<string, { item: AtlasItem; tileIndex: number }[]>();
|
||||
for (const item of items) {
|
||||
for (const [atlasId, config] of Object.entries(this.atlases)) {
|
||||
const tileIndex = config.ids.indexOf(item.icon);
|
||||
if (tileIndex === -1) continue;
|
||||
let arr = byAtlas.get(atlasId);
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
byAtlas.set(q.atlasId, arr);
|
||||
byAtlas.set(atlasId, arr);
|
||||
}
|
||||
arr.push({ item, tileIndex });
|
||||
break;
|
||||
}
|
||||
arr.push(q);
|
||||
}
|
||||
|
||||
for (const [atlasId, atlasQuads] of byAtlas) {
|
||||
for (const [atlasId, entries] of byAtlas) {
|
||||
const texture = this.textureCache.get(atlasId);
|
||||
const config = this.atlases[atlasId];
|
||||
if (!texture || !config) continue;
|
||||
this.group.add(buildMesh(atlasQuads, config, texture));
|
||||
this.group.add(buildMesh(entries, config, texture));
|
||||
}
|
||||
WebGLLayer.rerender();
|
||||
}
|
||||
|
|
@ -113,20 +118,20 @@ export class TextureAtlasLayer {
|
|||
}
|
||||
|
||||
function buildMesh(
|
||||
quads: AtlasQuad[],
|
||||
entries: Array<{ item: AtlasItem; tileIndex: number }>,
|
||||
atlas: AtlasConfig,
|
||||
texture: Texture,
|
||||
): Mesh {
|
||||
const { cols, rows } = atlas;
|
||||
const positions = new Float32Array(quads.length * 4 * 3);
|
||||
const uvs = new Float32Array(quads.length * 4 * 2);
|
||||
const indices = new Uint32Array(quads.length * 6);
|
||||
const positions = new Float32Array(entries.length * 4 * 3);
|
||||
const uvs = new Float32Array(entries.length * 4 * 2);
|
||||
const indices = new Uint32Array(entries.length * 6);
|
||||
|
||||
let vi = 0,
|
||||
ii = 0;
|
||||
for (const q of quads) {
|
||||
const col = q.tileIndex % cols;
|
||||
const row = Math.floor(q.tileIndex / cols);
|
||||
for (const { item: q, tileIndex } of entries) {
|
||||
const col = tileIndex % cols;
|
||||
const row = Math.floor(tileIndex / cols);
|
||||
const u0 = col / cols,
|
||||
u1 = (col + 1) / cols;
|
||||
const v0 = row / rows,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { RELIEF_ATLASES, RELIEF_SYMBOLS } from "../config/relief-config";
|
||||
import { RELIEF_ATLASES } from "../config/relief-config";
|
||||
import type { ReliefIcon } from "../modules/relief-generator";
|
||||
import { generateRelief } from "../modules/relief-generator";
|
||||
import { TextureAtlasLayer } from "../modules/texture-atlas-layer";
|
||||
import { byId } from "../utils";
|
||||
|
||||
const terrainLayer = new TextureAtlasLayer("terrain", RELIEF_ATLASES);
|
||||
const layer = new TextureAtlasLayer("terrain", RELIEF_ATLASES);
|
||||
|
||||
function drawSvg(icons: ReliefIcon[], parentEl: HTMLElement): void {
|
||||
parentEl.innerHTML = icons
|
||||
.map(
|
||||
(r) =>
|
||||
`<use href="${r.href}" data-id="${r.i}" x="${r.x}" y="${r.y}" width="${r.s}" height="${r.s}"/>`,
|
||||
`<use href="#${r.icon}" data-id="${r.i}" x="${r.x}" y="${r.y}" width="${r.s}" height="${r.s}"/>`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
|
@ -29,24 +29,12 @@ window.drawRelief = (
|
|||
if (type === "svg") {
|
||||
drawSvg(icons, parentEl);
|
||||
} else {
|
||||
terrainLayer.draw(resolveQuads(icons));
|
||||
layer.draw(icons);
|
||||
}
|
||||
};
|
||||
|
||||
function resolveQuads(icons: ReliefIcon[]) {
|
||||
return icons.map((r) => {
|
||||
const id = r.href.startsWith("#") ? r.href.slice(1) : r.href;
|
||||
for (const [set, { ids }] of Object.entries(RELIEF_SYMBOLS)) {
|
||||
const tileIndex = ids.indexOf(id);
|
||||
if (tileIndex !== -1)
|
||||
return { atlasId: set, x: r.x, y: r.y, s: r.s, tileIndex };
|
||||
}
|
||||
throw new Error(`Relief: unknown symbol href "${r.href}"`);
|
||||
});
|
||||
}
|
||||
|
||||
window.undrawRelief = () => {
|
||||
terrainLayer.clear();
|
||||
layer.clear();
|
||||
const terrainEl = byId("terrain");
|
||||
if (terrainEl) terrainEl.innerHTML = "";
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue