# Fog by height

> useFog({ height: { top, bottom } }): the low pillars of a valley sink into a sea of mist, however near.

Example `fog-height`, running at https://nacatamalon.com/en/examples/fog-height. The whole catalogue: https://nacatamalon.com/llms/examples.md.

`useFog({ height: { top, bottom } })` fades things below `top` and hides them at `bottom`, however near they are: the mist at the foot of a cliff, a pit with no bottom to see. With `distance: false` it is the only fog, and the valley is clear across.

It joins the fog by distance (`near`, `far`): at each corner the denser of the two wins, so what is low sinks into the mist and what is far is lost in the haze. With `top` under `bottom` it runs the other way, into a cloud.

Every field of the fog is read every frame: `fog.enabled`, `fog.distance`, `fog.height.top` and `fog.height.bottom` change it at once, or through a tween.

Click the preview first so it hears the keyboard. 1, 2 and 3 switch between no fog, fog by height and both; the up and down arrows raise and lower the mist.

## Code

`index.ts`:

```ts
import { createGame } from 'nacatamalon';
import * as Settings from './settings';
import { Valley } from './scenes/demo';

const game = createGame('#app', {
    width: Settings.WIDTH,
    height: Settings.HEIGHT,
    background: Settings.SKY,
    scaling: 'integer'
});

// Declare Scenes
game({
    Valley
});
```

`components/terrain.ts`:

```ts
import { createMaterial, createMesh, getColor, useConeGeometry, useCubeGeometry, useCylinderGeometry } from 'nacatamalon';
import * as Settings from '../settings';

export const Terrain = () => {
    let seed = Settings.SEED;
    const rand = () => (seed = (seed * 16807) % 2147483647) / 2147483647;

    const paint = (hex: string) => createMaterial({
        shader: 'mesh3d',
        tint: getColor(hex)
    });

    const grass = paint('#5f9e5a');
    const rock = paint('#7a6f86');
    const wood = paint('#6b4a32');
    const leaf = paint('#3f7d4c');

    const block = useCubeGeometry({
        width: 1,
        height: 1,
        depth: 1
    });

    const trunk = useCylinderGeometry({
        radius: 0.08,
        height: 0.5,
        segments: 6
    });

    const crown = useConeGeometry({
        radius: 0.42,
        height: 1.1,
        segments: 7
    });

    const tree = (x: number, y: number, z: number) => {
        createMesh({
            geometry: trunk,
            material: wood,
            transform: {
                x,
                y: y + 0.25,
                z
            }
        });
        createMesh({
            geometry: crown,
            material: leaf,
            transform: {
                x,
                y: y + 0.95,
                z
            }
        });
    };

    // The ledge the camera stands on.
    createMesh({
        geometry: block,
        material: grass,
        transform: {
            x: 0,
            y: -0.5,
            z: 8.6,
            scaleX: 30,
            scaleY: 1,
            scaleZ: 3
        }
    });
    createMesh({
        geometry: block,
        material: rock,
        transform: {
            x: 0,
            y: -6,
            z: 8.6,
            scaleX: 30,
            scaleY: 10,
            scaleZ: 2.9
        }
    });

    for (const [x, z] of [[-5.5, 8], [-4.2, 7.6], [4.6, 7.8], [6, 8.4]] as const) {
        tree(x, 0, z);
    }

    // The valley: pillars of every height, a few tall, most well down, each topped with grass.
    for (let row = 0;row < 9;row++) {
        for (let col = -7;col <= 7;col++) {
            const z = 4.5 - row * 2;
            const x = col * 1.7 + (row % 2) * 0.85 + (rand() - 0.5) * 0.6;
            const top = rand() < 0.22 ? -0.4 - rand() * 1.2 : -3 - rand() * 7;
            const width = 1.1 + rand() * 0.6;

            createMesh({
                geometry: block,
                material: rock,
                transform: {
                    x,
                    y: top - 10,
                    z,
                    scaleX: width,
                    scaleY: 20,
                    scaleZ: width
                }
            });
            createMesh({
                geometry: block,
                material: grass,
                transform: {
                    x,
                    y: top + 0.05,
                    z,
                    scaleX: width,
                    scaleY: 0.1,
                    scaleZ: width
                }
            });

            if (rand() > 0.5) {
                tree(x, top + 0.1, z);
            }
        }
    }
};
```

`scenes/demo.ts`:

```ts
import { createScene, createText, useCamera3d, useFog, useKeyboard, useDirectionalLight, useUpdate, useSpawn } from 'nacatamalon';
import * as Settings from '../settings';

import { Terrain } from '../components/terrain';

export const Valley = () => {
    useCamera3d({
        projection: 'perspective',
        fov: 55,
        transform: {
            y: 6,
            z: 11,
            rotationX: -0.55
        }
    });
    useDirectionalLight({
        color: '#ffe2b8',
        intensity: 1,
        ambient: 0.28,
        transform: {
            rotationX: -0.8,
            rotationY: 0.6
        }
    });

    useSpawn(Terrain)();

    // One fog, changed by writing into it: every field is read every frame.
    const fog = useFog({
        color: Settings.SKY,
        near: 8,
        far: 26,
        distance: false,
        height: {
            top: -1.8,
            bottom: -6.5
        }
    });

    const label = createText({
        text: '',
        tint: '#3a3150',
        transform: {
            x: 8,
            y: 8
        }
    });

    createText({
        text: '1-3 FOG   UP/DOWN MIST',
        tint: '#6b6280',
        transform: {
            x: 8,
            y: 20
        }
    });

    const show = (mode: 1|2|3) => {
        fog.enabled = mode !== 1;
        fog.distance = mode === 3;
        label.text = ['NO FOG', 'BY HEIGHT', 'BY HEIGHT AND DISTANCE'][mode - 1]!;
    };

    show(2);

    const keys = useKeyboard();

    useUpdate((delta) => {
        if (keys.justPressed('1')) {
            show(1);
        }

        if (keys.justPressed('2')) {
            show(2);
        }

        if (keys.justPressed('3')) {
            show(3);
        }

        // The whole band moves: where the mist starts and where it is complete.
        const rise = (keys.isDown('ArrowUp') ? 1 : 0) - (keys.isDown('ArrowDown') ? 1 : 0);

        fog.height.top += rise * 2 * delta;
        fog.height.bottom += rise * 2 * delta;
    });

    return createScene();
};
```

`settings.ts`:

```ts
import { getColor } from 'nacatamalon';

export const WIDTH = 480;
export const HEIGHT = 270;
export const SEED = 7;
export const SKY = getColor('#b8b0d8');
```
