Super Chunks API

advanced scripting

Voxel Play 4 · Scripting / API

By default, Super Chunks read the distant terrain from the 3D Terrain Graph generator. If your world comes from your own data, for example a pre-baked map or terrain loaded from files, you can provide your own sampler so the distant terrain shows your data instead. This also works when the world has no terrain generator.

Assigning a Sampler

Assign a function to superChunkSamplerFactory on the Voxel Play Environment. The function receives the environment and returns a new object that implements ISuperChunkSampler:

env.superChunkSamplerFactory = e => new MySampler(myData);

Super Chunks are built on several background threads and the factory is called once for each thread, so it should return a new sampler every time. Assign it from the main thread. You can assign it at any time, and the distant terrain restarts with the new sampler on the next frame. Set it to null to go back to the built-in 3D Terrain Graph sampler.

The ISuperChunkSampler Interface

MethodDescription
int SampleColumn (double x, double z, ColumnInterval[] intervals, out double waterY, out ushort waterTypeIndex)Fills the solid intervals of the column at world position x, z from top to bottom and returns how many it wrote. It also returns the water level and the water voxel of the column. Return a waterTypeIndex of 0 when the column has no water.
bool TryGetTree (double x, double z, double groundY, out SuperChunkTreeCandidate tree)Returns the tree that grows on the ground of the column, if any. groundY is the top of the first interval. Return false if you do not need distant trees.

Columns are sampled every few voxels depending on the Super Chunks Quality, so the distant terrain is a coarse version of your data.

Column Intervals

Each ColumnInterval is a solid part of the column. A column with flat ground needs one interval, while a column with an overhang or a floating island needs one interval for each solid part, ordered from the highest to the lowest.

FieldDescription
yTopTop of the solid part, in world units.
yBottomBottom of the solid part, in world units.
topTypeIndexVoxel used for the top face.
sideTypeIndexVoxel used for the walls. 0 uses the top voxel.
bottomTypeIndexVoxel used for the bottom face.

The voxel values are the index of voxel definitions registered in the environment, for example env.GetVoxelDefinition("VoxelGrass").index.

When the column has water, the intervals whose top is at or above the water level are drawn as land and the rest are drawn as seafloor under the water. The water surface is only drawn on columns that have no land. The intervals array has room for 16 entries, and the distant terrain keeps up to 8 land intervals and 8 seafloor intervals per column.

Distant Trees

When Include Trees and Enable Trees are on, TryGetTree is called for columns that have land. Fill the SuperChunkTreeCandidate with the position where the tree stands on the ground, the tree model definition and a rotation from 0 to 3 in quarter turns.

Rules

  • SampleColumn and TryGetTree run on background threads, so they should only read data that is safe to access from other threads and should not call the Unity API. Copy what you need, such as the heights of a texture, into regular arrays on the main thread before assigning the factory.
  • Keep your terrain between the minHeight and maxHeight of the world's terrain generator, which is -255 to 255 when no generator is assigned. The distant terrain uses this range to refresh after voxel edits, and it does not draw the bottom face of intervals that reach minHeight.
  • When a voxel changes, the Super Chunks over that chunk are rebuilt with the data your sampler returns, so you should also update your data if you want edits to stay visible once the player moves away.

Example

This script shows a baked heightmap in the distant terrain. The texture must be readable, and each pixel is one voxel starting at world position 0, 0.

using UnityEngine;
using VoxelPlay;

// Shows a baked heightmap in the distant terrain
public class BakedDistantTerrain : MonoBehaviour {

    public Texture2D heightmap;
    public float maxTerrainHeight = 100;
    public float waterLevel = 20;
    public VoxelDefinition grass, dirt, water;

    void Start () {
        // Copy the heights on the main thread
        Color[] pixels = heightmap.GetPixels();
        float[] heights = new float[pixels.Length];
        for (int k = 0; k < pixels.Length; k++) {
            heights[k] = Mathf.Round(pixels[k].r * maxTerrainHeight);
        }
        int width = heightmap.width;
        int height = heightmap.height;

        VoxelPlayEnvironment env = VoxelPlayEnvironment.instance;
        env.superChunkSamplerFactory = e => new BakedSampler(heights, width, height, grass.index, dirt.index, water.index, waterLevel, e.world.terrainGenerator.minHeight);
    }
}

class BakedSampler : ISuperChunkSampler {

    readonly float[] heights;
    readonly int width, height;
    readonly ushort grass, dirt, water;
    readonly double waterLevel, bottom;

    public BakedSampler (float[] heights, int width, int height, ushort grass, ushort dirt, ushort water, double waterLevel, double bottom) {
        this.heights = heights;
        this.width = width;
        this.height = height;
        this.grass = grass;
        this.dirt = dirt;
        this.water = water;
        this.waterLevel = waterLevel;
        this.bottom = bottom;
    }

    public int SampleColumn (double x, double z, ColumnInterval[] intervals, out double waterY, out ushort waterTypeIndex) {
        waterY = waterLevel;
        waterTypeIndex = 0;
        int px = (int)System.Math.Floor(x);
        int pz = (int)System.Math.Floor(z);
        if (px < 0 || pz < 0 || px >= width || pz >= height) return 0;

        double top = heights[pz * width + px];
        intervals[0] = new ColumnInterval {
            yTop = top,
            yBottom = bottom,
            topTypeIndex = grass,
            sideTypeIndex = dirt,
            bottomTypeIndex = dirt
        };
        if (top < waterLevel) waterTypeIndex = water;
        return 1;
    }

    public bool TryGetTree (double x, double z, double groundY, out SuperChunkTreeCandidate tree) {
        tree = default;
        return false;
    }
}

See Also

Was this page helpful?