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); });