import { describe, expect, it } from "vitest"; import { Document, NodeIO } from "@gltf-transform/core"; import { KHRTextureTransform } from "@gltf-transform/extensions"; import { dedup, prune } from "@gltf-transform/functions"; import sharp from "sharp"; import { EXT_MATERIALS_BUMP, EXTMaterialsBump } from "../tools/assets/bumpExtension.mjs"; /** 8 × 8 px mit wechselnden Werten — prune() ersetzt einfarbige Texturen sonst durch Faktoren. */ function pattern() { const data = Buffer.alloc(8 * 8 * 3); for (let i = 0; i < data.length; i++) data[i] = (i * 37) & 0xff; return sharp(data, { raw: { width: 8, height: 8, channels: 3 } }).png().toBuffer(); } /** Ein Dreieck mit dem Material cont_a_side, Bump-Stärke 2,2 und Wellenteilung [3.2, 1]. */ async function bumpDocument() { const document = new Document(); document.createBuffer(); const bumpExtension = document.createExtension(EXTMaterialsBump); const transformExtension = document.createExtension(KHRTextureTransform); const texture = document.createTexture("wellblech").setImage(await pattern()).setMimeType("image/png"); const bump = bumpExtension.createBump().setBumpFactor(2.2).setBumpTexture(texture); bump.getBumpTextureInfo().setExtension("KHR_texture_transform", transformExtension.createTransform().setScale([3.2, 1])); const material = document.createMaterial("cont_a_side").setExtension(EXT_MATERIALS_BUMP, bump); const positions = document.createAccessor().setType("VEC3").setArray(new Float32Array([0, 0, 0, 1, 0, 0, 0, 1, 0])); const primitive = document.createPrimitive().setMaterial(material).setAttribute("POSITION", positions); document.createScene().addChild(document.createNode().setMesh(document.createMesh().addPrimitive(primitive))); return document; } /** JSON-Teil einer .glb: 12 Byte Kopf, dann Länge und Typ des ersten Blocks, dann das JSON. */ function glbJson(glb) { const view = new DataView(glb.buffer, glb.byteOffset, glb.byteLength); return JSON.parse(new TextDecoder().decode(glb.subarray(20, 20 + view.getUint32(12, true)))); } describe("EXT_materials_bump", () => { const io = () => new NodeIO().registerExtensions([EXTMaterialsBump, KHRTextureTransform]); // Bruch: @gltf-transform verwirft die Erweiterung (Wellblech flach), prune entfernt die Textur als // unbenutzt, oder die Wellenteilung an der bumpTexture geht verloren (Wellen 3,2-mal zu breit). it("übersteht dedup, prune, Schreiben und Einlesen mit Stärke, Textur und Teilung", async () => { const document = await bumpDocument(); await document.transform(dedup(), prune()); const read = await io().readBinary(await io().writeBinary(document)); const bump = read.getRoot().listMaterials()[0].getExtension(EXT_MATERIALS_BUMP); expect(bump.getBumpFactor()).toBe(2.2); expect(bump.getBumpTexture().getSize()).toEqual([8, 8]); expect(bump.getBumpTextureInfo().getExtension("KHR_texture_transform").getScale()).toEqual([3.2, 1]); }); // Bruch: Feldnamen oder Aufbau weichen von der Spezifikation ab — three.js (GLTFLoader) liest // genau materials[i].extensions.EXT_materials_bump.bumpFactor und .bumpTexture.index. it("schreibt das JSON so, wie three.js es liest", async () => { const json = glbJson(await io().writeBinary(await bumpDocument())); expect(json.extensionsUsed).toContain(EXT_MATERIALS_BUMP); const def = json.materials[0].extensions[EXT_MATERIALS_BUMP]; expect(def.bumpFactor).toBe(2.2); expect(json.textures[def.bumpTexture.index]).toBeDefined(); expect(def.bumpTexture.extensions.KHR_texture_transform.scale).toEqual([3.2, 1]); }); });