#!/usr/bin/env python3 """Copy only tiles for one LOD and spatial window from a VFMSH scene.""" import argparse import resource import shutil import struct def main(): parser = argparse.ArgumentParser() parser.add_argument("source") parser.add_argument("destination") parser.add_argument("--lod", type=int, required=True) parser.add_argument("--bounds", nargs=6, type=float, required=True, metavar=("XMIN", "XMAX", "YMIN", "YMAX", "ZMIN", "ZMAX")) args = parser.parse_args() if not 0 <= args.lod <= 8: parser.error("--lod must be between 0 and 8") x_min, x_max, y_min, y_max, z_min, z_max = args.bounds if x_max <= x_min or y_max <= y_min or z_max <= z_min: parser.error("bounds must have positive extents") selected = [] with open(args.source, "rb") as src: prefix = src.read(60) if len(prefix) != 60 or prefix[:8] != b"VFMSH001": raise SystemExit("source has an invalid VFMSH001 header") version, voxel_um, tile_count = struct.unpack_from("<3I", prefix, 8) if version != 1 or voxel_um == 0 or tile_count > 1_000_000: raise SystemExit("source has an unsupported scene header") for _ in range(tile_count): header = src.read(36) if len(header) != 36: raise SystemExit("truncated tile header") key = struct.unpack_from("<4i", header) _, _, vertex_count, index_count, mask_count = struct.unpack_from("<5I", header, 16) payload_offset = src.tell() payload_size = vertex_count * 24 + index_count * 4 + mask_count extent = 32.0 * (1 << key[3]) * voxel_um / 1_000_000.0 tx0, ty0, tz0 = key[0] * extent, key[1] * extent, key[2] * extent intersects = (key[3] == args.lod and tx0 <= x_max and tx0 + extent >= x_min and ty0 <= y_max and ty0 + extent >= y_min and tz0 <= z_max and tz0 + extent >= z_min) if intersects: selected.append((header, payload_offset, payload_size)) src.seek(payload_size, 1) if src.read(1): raise SystemExit("source scene has trailing bytes") if not selected: raise SystemExit("no tiles matched the requested LOD and bounds") with open(args.source, "rb") as src, open(args.destination, "wb") as dst: prefix = bytearray(src.read(60)) struct.pack_into("