.. _3d-texture-format: 3D texture format ================= This section describes the 3D texture format used by Gaia Sky to represent volumes. A volume is a raw dump of a voxel grid, optionally gzip-compressed, together with an optional plain-text sidecar that declares the grid resolution and the box the grid occupies in world space. Gaia Sky binds the voxels as a 3D texture, so any 3D tool that can write an RGBA8 grid can produce one. Volumes are loaded by the `VolumeTextureLoader `__. File pair --------- A volume is two files that share a base name:: _rgba.raw.gz the voxels _metadata.txt the grid resolution and the world-space box The voxel file is gzipped if its extension is ``.gz``, and read as-is otherwise. The recognised suffixes, which the loader strips to recover ````, are ``_density``, ``_color`` and ``_rgba``, each followed by ``.raw`` or ``.raw.gz``. The same suffix also terminates the metadata file name. Voxel format ------------ Voxels are stored as unsigned bytes, contiguously, without padding or a header. The number of bytes per voxel selects the format: ============== =========== ================ ============ Suffix Bytes/voxel GL format Density byte ============== =========== ================ ============ ``_density`` 1 ``GL_R8`` ``0`` (``.r``) ``_color`` 3 ``GL_RGB8`` none ``_rgba`` 4 ``GL_RGBA8`` ``3`` (``.a``) ============== =========== ================ ============ The decision is taken from the file length, not from the suffix: the byte count divided by the number of voxels must be 1, 3 or 4. Each voxel is written in the order R, G, B, A. In a colour+density volume the alpha channel carries the density, which is the quantity the renderer ray-marches and integrates. The galaxy volumes use the alpha channel as brightness and the RGB channels as the chromaticity of the light, so the emitted radiance is the product ``RGB * A``. An occlusive volume (dust) uses the same layout, with ``A`` holding the normalised density. Voxels are written in C order, ``(W, H, D)``, so the third axis varies fastest: the row of ``D`` voxels along that axis is contiguous, and the whole file is the concatenation of the ``W * H`` such rows. Metadata file ------------- The sidecar is plain text, one ``key: value`` per line. Lines without a colon are ignored, so ``#`` comments and free-form extra keys are harmless. Only two keys are read: - ``Grid size:`` --- the grid resolution. Either a single edge, which means a cube (``400``), or three extents separated by ``x``, ``*`` or a comma (``400x400x40``, ``400 * 400 * 40``, ``400, 400, 40``). - ``Volume bounds:`` (or ``World bounds:``) --- the box in world space, as ``[x0, y0, z0] to [x1, y1, z1]``. It is the box the shader intersects, and the texture coordinate of a point is its position normalised over it, per axis. A typical file:: # Generated by particles-to-volume.py Volume: emissive Grid size: 399x40x400 Galactic grid (x, y, z): 399x400x40 World size: [0.998040, 0.099562, 1.000000] Volume bounds: [-0.473301, -0.048570, -0.475536] to [0.477213, 0.046252, 0.476845] Voxel size: 0.00250000 (cubic; the largest box side is 1, in units of the object's size) ``Data bounds:`` is the extent of the source particles rather than of the box, and is ignored by the loader. The ``Galactic grid`` and ``World size`` lines above are informational. .. note:: The grid and the bounds are in the file's own ``(W, H, D)`` order, i.e. the order the voxels are written in. The loader reverses the three extents and the three bounds components together when it hands them to OpenGL, so a file is described entirely in the axis order it is dumped in. This only matters for a grid that is not a cube. Declaring ``Grid size:`` is what makes a non-cubic grid possible; without it, the resolution can only be inferred as a cube. When the sidecar is missing or declares no grid, the resolution is inferred from the file length as a cube: ``n`` voxels per side for a density volume, ``n`` for a colour volume (``3 * n^3`` bytes) and ``n`` for a colour+density one (``4 * n^3`` bytes). The box defaults to the unit cube centred on the origin, ``[-0.5, -0.5, -0.5]`` to ``[0.5, 0.5, 0.5]``. Loading ------- On load, Gaia Sky crops the grid to the range of voxels that hold any density, plus one voxel of padding, and builds a brick occupancy grid from it, both of which speed up the ray march. Neither changes the format, and the cropped grid is mapped onto the same world-space box, so writing the empty shell in the file costs file size and load time only. The galaxy volumes shipped with Gaia Sky are produced from particle data by ``particles-to-volume.py``, in the core repository under ``scripts/other/volumes/``.