let map, map_size = 128, map_load_container = async (path) => { /* Parse map container format typedef struct { u8 x, y, z; u8 sx, sy, sz; } block_t; typedef struct { u8 sentinel; u8 tex; } block_texture_t; typedef struct { char type; u8 x, y, z; u8 data1, data2; } entity_t; struct { u16 blocks_size; block_t blocks[]; u16 num_entities; entity_t entities[num_entities]; } map_data; Block data is interleaved with the block_texture_t struct to denote the texture index to use for the following blocks. */ let data = new Uint8Array(await (await fetch(path)).arrayBuffer()), maps = []; for (let i = 0; i < data.length;) { let blocks_size = data[i++] | (data[i++] << 8), cm = new Uint8Array(map_size * map_size * map_size >> 3), // collision map nm = new Uint8Array(map_size * map_size * map_size >> 3), // nav map b = data.subarray(i, i += blocks_size), r = [], t; // Parse block data and construct geometry and the collision map for (let j = 0; j < b.length;) { // First value is either the x coordinate or a texture change // sentinel value (255) followed by the texture coordinate if (b[j] == 255) { j++; t = b[j++]; } let x = b[j++], y = b[j++], z = b[j++], sx = b[j++], sy = b[j++], sz = b[j++]; // Submit the block to the render buffer; we get the vertex offset // of this block within the buffer back, so we can draw it later r.push({ t, b: r_push_block( x << 5, y << 4, z << 5, sx << 5, sy << 4, sz << 5, t ) }); // The collision map is a bitmap; 8 x blocks per byte for (let cz = z; cz < z + sz; cz++) { for (let cy = y; cy < y + sy; cy++) { for (let cx = x; cx < x + sx; cx++) { cm[ ( cz * map_size * map_size + cy * map_size + cx ) >> 3 ] |= 1 << (cx & 7); } } } } // Slice of entity data; we parse it when we actually spawn // the entities in map_init() let num_entities = data[i++] | (data[i++] << 8), e = data.subarray(i, i += num_entities * 6 /*sizeof(entity_t)*/); maps.push({cm, e, r}); } return maps; }, map_init = (m) => { map = m; // Entity Id to class - must be consistent with map_packer.c line ~900 let spawn_class = [ /* 00 */ entity_player_t, /* 01 */ entity_enemy_grunt_t, /* 02 */ entity_enemy_enforcer_t, /* 03 */ entity_enemy_ogre_t, /* 04 */ entity_enemy_zombie_t, /* 05 */ entity_enemy_hound_t, /* 06 */ entity_pickup_nailgun_t, /* 07 */ entity_pickup_grenadelauncher_t, /* 08 */ entity_pickup_health_t, /* 09 */ entity_pickup_nails_t, /* 10 */ entity_pickup_grenades_t, /* 11 */ entity_barrel_t, /* 12 */ entity_light_t, /* 13 */ entity_trigger_level_t, /* 14 */ entity_door_t, /* 15 */ entity_pickup_key_t, /* 16 */ entity_torch_t, ]; // Parse entity data and spawn all entities for this map for (let i = 0; i < map.e.length;) { let type = spawn_class[m.e[i++]]; game_spawn( type, vec3(m.e[i++] * 32, m.e[i++] * 16, m.e[i++] * 32), m.e[i++], m.e[i++] ); } }, map_block_at = (x, y, z) => map.cm[ ( z * map_size * map_size + y * map_size + x ) >> 3 ] & (1 << (x & 7)), map_trace = (a, b) => { let diff = vec3_sub(b, a), step_dir = vec3_mulf(vec3_normalize(diff), 16), steps = vec3_length(diff)/16; for (let i = 0; i < steps; i++) { a = vec3_add(a, step_dir); if (map_block_at(a.x >> 5, a.y >> 4, a.z >> 5)) { return a; } } return null; }, map_block_at_box = (box_start, box_end) => { for (let z = box_start.z >> 5; z <= box_end.z >> 5; z++) { for (let y = box_start.y >> 4; y <= box_end.y >> 4; y++) { for (let x = box_start.x >> 5; x <= box_end.x >> 5; x++) { if (map_block_at(x, y, z)) { return true; } } } } return false; }, map_draw = () => { let p = vec3(); for (let r of map.r) { r_draw(p, 0, 0, r.t, r.b,r.b,0,36); } };