# Occlusion

> A model's creases darker, as its author saw them: a glTF's occlusion read by itself, and occlusion on createMesh painted in pixels.

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

The creases of a model darker, as its author saw them in the modelling program. A glTF's `occlusionTexture` is read by itself, with its strength and either of its first two picture coordinates; in code it is `occlusion` on `createMaterial` or `createMesh`, the picture alone or `{ texture, strength, uv, wrap }`.

It lowers only the light from all around, the ambient: the sun goes round, and a crease turned to it is lit like anywhere else, while the same crease in the shade is darker than the surface beside it. The left bottle keeps what its file brings, the right one has it taken away, and the crate in the middle has its creases painted in pixels.

Click the preview first so it hears the keyboard. Up and down change how much is applied.

## Code

`index.ts`:

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

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

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

`art/creases.ts`:

```ts
import { createPixelTexture, createPixels, fillRect } from 'nacatamalon/pixels';

export const createCreases = () => {
    const creases = createPixels(32, 32);

    fillRect(creases, 0, 0, 32, 32, '#303030');
    fillRect(creases, 4, 4, 24, 24, '#ffffff');
    fillRect(creases, 15, 4, 2, 24, '#707070');
    fillRect(creases, 4, 15, 24, 2, '#707070');

    return createPixelTexture(creases);
};
```

`components/comparison.ts`:

```ts
import { createMesh, createModel, createText, useCubeGeometry, useKeyboard, useLoadModel, useUpdate } from 'nacatamalon';

import { createCreases } from '../art/creases';

export const Comparison = () => {
    // One file, two models: a model's pieces each wear a material of their own.
    const bottle = useLoadModel({
        src: '/example-assets/assets/3d/gltf/WaterBottle/WaterBottle.gltf'
    });

    const size = {
        scaleX: 17,
        scaleY: 17,
        scaleZ: 17
    };

    const kept = createModel({
        model: bottle,
        transform: {
            x: -2.6,
            ...size
        }
    });

    const taken = createModel({
        model: bottle,
        transform: {
            x: 2.6,
            ...size
        }
    });

    // White is open, dark is shut in: a frame round each face and a cross where its planks meet.
    const crate = createMesh({
        geometry: useCubeGeometry(),
        tint: '#c9a46a',
        occlusion: createCreases(),
        transform: {
            y: -0.2,
            scaleX: 1.4,
            scaleY: 1.4,
            scaleZ: 1.4
        },
    });

    createText({
        text: 'WITH',
        transform: {
            x: 70,
            y: 16
        }
    });
    createText({
        text: 'IN CODE',
        transform: {
            x: 250,
            y: 16
        }
    });
    createText({
        text: 'WITHOUT',
        transform: {
            x: 440,
            y: 16
        }
    });

    const label = createText({
        text: '',
        transform: {
            x: 16,
            y: 372
        }
    });

    const keys = useKeyboard();
    let strength = 1;

    useUpdate((delta, t) => {
        crate.transform.rotationY += delta * 0.3;

        for (const model of [kept, taken]) {
            model.transform.rotationY = Math.sin(t * 0.3) * 0.6;
        }

        if (keys.isDown('ArrowUp')) {
            strength = Math.min(1, strength + delta * 0.5);
        }

        if (keys.isDown('ArrowDown')) {
            strength = Math.max(0, strength - delta * 0.5);
        }

        // The pieces arrive with the file. A material keeps its occlusion in the long form,
        // { texture, strength, uv, wrap }, so its strength is changed there.
        for (const part of kept.parts) {
            if (part.material.occlusion) {
                part.material.occlusion.strength = strength;
            }
        }

        for (const part of taken.parts) {
            part.material.occlusion = null;
        }

        if (crate.material.occlusion) {
            crate.material.occlusion.strength = strength;
        }

        label.text = `STRENGTH ${strength.toFixed(2)}   UP/DOWN`;
    });
};
```

`scenes/demo.ts`:

```ts
import { createScene, useCamera3d, useDirectionalLight, useUpdate, useSpawn } from 'nacatamalon';

import { Comparison } from '../components/comparison';

export const Scene = () => {
    useCamera3d({
        projection: 'perspective',
        fov: 40,
        transform: {
            y: 0.1,
            z: 9
        }
    });

    const sun = useDirectionalLight({
        color: '#fff4e6',
        intensity: 0.5,
        ambient: 0.8,
        transform: {
            rotationX: -0.45
        }
    });

    useSpawn(Comparison)();
    useUpdate((_delta, time) => {
        sun.transform.rotationY = time * 0.5;
    });

    return createScene();
};
```

`settings.ts`:

```ts
export const WIDTH = 560;
export const HEIGHT = 400;
```
