Files
bworld/tests/block_models_test.ts
T
2026-09-25 22:49:27 -03:00

75 lines
3.2 KiB
TypeScript

import { assert, assertEquals } from "@std/assert";
import { bake_model, block_variant, BUILTIN_MODELS, type ModelJson, resolve_texture } from "$/common/block_models.ts";
import { block_from_json } from "$/common/mod_data.ts";
import { set_state_value } from "$/common/utils.ts";
const cube = BUILTIN_MODELS["engine:cube"];
Deno.test("cubes resolve the old texture shapes", () => {
assertEquals(resolve_texture("#top", "a:all", cube), "a:all");
const pillar = { top: "a:top", bottom: "a:bottom", side: "a:side" };
assertEquals(["#top", "#bottom", "#front", "#side"].map((t) => resolve_texture(t, pillar, cube)), [
"a:top",
"a:bottom",
"a:side",
"a:side",
]);
const furnace = { front: "a:front", side: "a:side" };
assertEquals(["#top", "#bottom", "#front"].map((t) => resolve_texture(t, furnace, cube)), [
"a:side",
"a:side",
"a:front",
]);
assertEquals(resolve_texture("#nothing", { crop: "a:crop" }), "engine:missing");
});
Deno.test("a cube bakes into six full faces that cull against their neighbors", () => {
const quads = bake_model(cube, "a:all");
assertEquals(quads.length, 6);
assert(quads.every((q) => q.flush && q.cull === q.face && q.texture === "a:all"));
// the top face, corners and uvs like the mesher always had
assertEquals(quads[0].positions, [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0]);
assertEquals(quads[0].uvs, [0, 16, 16, 16, 16, 0, 0, 0]);
});
Deno.test("crop and cross models are planes inside the block, seen from both sides", () => {
const crop = bake_model(BUILTIN_MODELS["engine:crop"], { crop: "a:wheat" });
assertEquals(crop.length, 8);
assert(crop.every((q) => !q.flush && q.cull === -1 && !q.shade && q.texture === "a:wheat"));
assertEquals(crop[0].positions.filter((_, i) => i % 3 === 0), [0.25, 0.25, 0.25, 0.25]);
const cross = bake_model(BUILTIN_MODELS["engine:cross"], { cross: "a:flower" });
assertEquals(cross.length, 4);
// rescaled so the diagonal planes stay as wide as the block, like minecraft's (0.8 to 15.2 pixels)
for (const quad of cross) {
for (const v of quad.positions) assert(v > -1e-6 && v < 1 + 1e-6, `${v} is outside the block`);
}
const xs = cross[0].positions.filter((_, i) => i % 3 === 0).map((v) => Math.round(v * 1000) / 1000);
assertEquals(new Set(xs), new Set([0.05, 0.95]));
});
Deno.test("turning a model moves its faces and cullfaces", () => {
const model: ModelJson = {
id: "a:half",
elements: [{
from: [0, 0, 0],
to: [16, 16, 8],
faces: { north: { texture: "a:x", cullface: "north" } },
}],
};
const [north] = bake_model(model, undefined, 0);
const [east] = bake_model(model, undefined, 90);
assertEquals([north.face, north.cull], [3, 3]);
assertEquals([east.face, east.cull], [5, 5]);
assert(east.flush);
});
Deno.test("variants pick textures by state", () => {
const { block } = block_from_json(JSON.parse(Deno.readTextFileSync("mods/bworld/blocks/wheat.json")).block);
assertEquals(block_variant(block, { age: 5 }).textures, { crop: "bworld:wheat_stage_5" });
assertEquals(block_variant(block, { age: 5 }).model, "engine:crop");
assertEquals(block_variant(block).textures, { crop: "bworld:wheat_stage_0" });
// states live above the numeric id
assertEquals(set_state_value(7, block, "age", 3), (3 << 16) | 7);
});