// Copyright (c) 2026 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT "use strict"; // DMF - Dusk Mesh Format // // Header (12 bytes): // [0-3] "DMF\0" magic // [4-7] uint32 version (little-endian) // [8-11] uint32 vertCount (little-endian) // // Followed by vertCount vertices, each 20 bytes: // [0-7] float32[2] uv (little-endian) // [8-19] float32[3] pos (little-endian) const DMF = (() => { const MAGIC = [0x44, 0x4d, 0x46, 0x00]; // "DMF\0" const VERSION = 1; const HEADER_SIZE = 12; const VERTEX_FLOATS = 5; // uv[2] + pos[3] const VERTEX_SIZE = VERTEX_FLOATS * 4; // Decode a DMF ArrayBuffer into { version, vertexCount, floats }, where // floats is a Float32Array of tightly interleaved [u, v, x, y, z] per // vertex - ready to hand straight to gl.bufferData(). function decode(buffer) { const bytes = new Uint8Array(buffer); const view = new DataView(buffer); if(bytes.length < HEADER_SIZE) throw new Error("File too small to be a valid DMF"); if( bytes[0] !== MAGIC[0] || bytes[1] !== MAGIC[1] || bytes[2] !== MAGIC[2] || bytes[3] !== MAGIC[3] ) { throw new Error("Invalid DMF magic bytes - not a DMF file"); } const version = view.getUint32(4, true); if(version !== VERSION) { throw new Error(`Unsupported DMF version: ${version}`); } const vertexCount = view.getUint32(8, true); const expected = HEADER_SIZE + vertexCount * VERTEX_SIZE; if(bytes.length < expected) { throw new Error(`DMF vertex data truncated (expected ${expected} bytes, got ${bytes.length})`); } const floats = new Float32Array(vertexCount * VERTEX_FLOATS); for(let i = 0; i < floats.length; i++) { floats[i] = view.getFloat32(HEADER_SIZE + i * 4, true); } return { version, vertexCount, floats }; } return Object.freeze({ decode, VERSION, HEADER_SIZE, VERTEX_FLOATS, VERTEX_SIZE, }); })();