Render surface maps directly and preserve their quality
This commit is contained in:
123
tests/surface-map.test.ts
Normal file
123
tests/surface-map.test.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { surfacePixels } from "../renderer/surface-map.mjs";
|
||||
import type { TerrainSnapshot } from "../shared/protocol";
|
||||
|
||||
function pixel(map: ReturnType<typeof surfacePixels>, x: number, z: number) {
|
||||
return [
|
||||
...map.data.subarray((z * map.width + x) * 4, (z * map.width + x + 1) * 4),
|
||||
];
|
||||
}
|
||||
|
||||
test("surface map keeps north up, negative coordinates, rectangular bounds and uncovered parchment", () => {
|
||||
const map = surfacePixels({
|
||||
bounds: { minX: -2, maxX: 2, minZ: -3, maxZ: -1 },
|
||||
coverage: [],
|
||||
blocks: [
|
||||
[-2, 64, -3, "minecraft:snow_block"],
|
||||
[1, 64, -2, "minecraft:grass_block"],
|
||||
[2, 90, -2, "minecraft:snow_block"], // half-open boundary
|
||||
[-2, 90, -1, "minecraft:snow_block"],
|
||||
],
|
||||
});
|
||||
assert.equal(map.width, 4);
|
||||
assert.equal(map.height, 2);
|
||||
assert.equal(map.columns, 2);
|
||||
const north = pixel(map, 0, 0);
|
||||
assert.equal(north[0], north[1]);
|
||||
assert.equal(north[1], north[2]);
|
||||
const south = pixel(map, 3, 1);
|
||||
assert.ok(south[1] > south[0] && south[0] > south[2]);
|
||||
const blank = pixel(map, 3, 0);
|
||||
assert.ok(blank[0] > blank[1] && blank[1] > blank[2]);
|
||||
assert.equal(blank[3], 255);
|
||||
});
|
||||
|
||||
test("the highest mapped block wins regardless of export order and glass does not obscure terrain", () => {
|
||||
const terrain: TerrainSnapshot = {
|
||||
bounds: { minX: 0, maxX: 1, minZ: 0, maxZ: 1 },
|
||||
coverage: [],
|
||||
blocks: [
|
||||
[0, 63, 0, "minecraft:dirt"],
|
||||
[0, 64, 0, "minecraft:grass_block[snowy=false]"],
|
||||
[0, 70, 0, "minecraft:glass"],
|
||||
[0, 71, 0, "minecraft:short_grass"],
|
||||
],
|
||||
};
|
||||
const first = surfacePixels(terrain);
|
||||
const reversed = surfacePixels({
|
||||
...terrain,
|
||||
blocks: [...terrain.blocks].reverse(),
|
||||
});
|
||||
assert.deepEqual(first.data, reversed.data);
|
||||
const rgb = pixel(first, 0, 0);
|
||||
assert.ok(rgb[1] > rgb[0] && rgb[0] > rgb[2]);
|
||||
});
|
||||
|
||||
test("depth shading separates shallow, deep and unknown-bed water without texture speckle", () => {
|
||||
const map = surfacePixels({
|
||||
bounds: { minX: 0, maxX: 3, minZ: 0, maxZ: 1 },
|
||||
coverage: [],
|
||||
blocks: [
|
||||
[0, 64, 0, "minecraft:water[level=0]"],
|
||||
[0, 63, 0, "minecraft:sand"],
|
||||
[1, 64, 0, "minecraft:water[level=0]"],
|
||||
[1, 52, 0, "minecraft:stone"],
|
||||
[2, 64, 0, "minecraft:water[level=0]"],
|
||||
],
|
||||
});
|
||||
const shallow = pixel(map, 0, 0),
|
||||
deep = pixel(map, 1, 0);
|
||||
assert.ok(shallow[2] > shallow[1] && shallow[1] > shallow[0]);
|
||||
assert.ok(shallow[2] > deep[2]);
|
||||
assert.deepEqual(deep, pixel(map, 2, 0));
|
||||
});
|
||||
|
||||
test("height relief shades slopes while flat ground stays uniform", () => {
|
||||
const blocks: TerrainSnapshot["blocks"] = [64, 64, 66, 64].map((y, z) => [
|
||||
0,
|
||||
y,
|
||||
z,
|
||||
"minecraft:stone",
|
||||
]);
|
||||
const map = surfacePixels({
|
||||
bounds: { minX: 0, maxX: 1, minZ: 0, maxZ: 4 },
|
||||
coverage: [],
|
||||
blocks,
|
||||
});
|
||||
assert.deepEqual(pixel(map, 0, 0), pixel(map, 0, 1));
|
||||
assert.ok(pixel(map, 0, 2)[0] > pixel(map, 0, 1)[0]);
|
||||
assert.ok(pixel(map, 0, 3)[0] < pixel(map, 0, 1)[0]);
|
||||
});
|
||||
|
||||
test("map cells use the dominant surface material instead of a textured average", () => {
|
||||
const blocks: TerrainSnapshot["blocks"] = [];
|
||||
for (let x = 0; x < 4; x++)
|
||||
for (let z = 0; z < 4; z++)
|
||||
blocks.push([
|
||||
x,
|
||||
64,
|
||||
z,
|
||||
x === 0 ? "minecraft:stone" : "minecraft:grass_block",
|
||||
]);
|
||||
const map = surfacePixels(
|
||||
{ bounds: { minX: 0, maxX: 4, minZ: 0, maxZ: 4 }, coverage: [], blocks },
|
||||
{ resolution: 1 },
|
||||
);
|
||||
assert.equal(map.width, 1);
|
||||
assert.equal(map.height, 1);
|
||||
const rgb = pixel(map, 0, 0);
|
||||
assert.ok(rgb[1] > rgb[0] && rgb[0] > rgb[2]);
|
||||
});
|
||||
|
||||
test("empty exports fail instead of producing a playable blank map", () => {
|
||||
assert.throws(
|
||||
() =>
|
||||
surfacePixels({
|
||||
bounds: { minX: 0, maxX: 16, minZ: 0, maxZ: 16 },
|
||||
coverage: [],
|
||||
blocks: [],
|
||||
}),
|
||||
/no mapped terrain/,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user