# Carry

> A vertex hook hands four numbers to its colour hook with carry: a sea foamed by how high its waves lifted each corner, a block banded in its own space, and a crystal that glints corner by corner.

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

Four numbers a material's vertex hook works out at each corner and hands to its colour hook, smeared across the triangle the way the light is. In the shader composer, `carry` on `compileShader`, read with `composerCarry()`; by hand, the vertex hook writes `carry = vec4<f32>(...)` and the colour hook reads `ctx.carry`.

The sea carries how high each wave lifted a corner, so its crests foam. The block carries where each corner is in its own space, so its bands stay on it as it turns, cut out, shadow and all. The crystal carries a number of each corner's own, so it glints corner by corner, the glint running across its facets.

## 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: '#0b1830',
});

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

`components/exhibit.ts`:

```ts
import { createMaterial, createMesh, useCubeGeometry, useIcoSphereGeometry, usePlaneGeometry, useUpdate } from 'nacatamalon';

import { BANDS, CRYSTAL, SEA } from '../shaders/carry';

export const Exhibit = () => {
    createMesh({
        geometry: usePlaneGeometry({
            width: 16,
            depth: 8,
            widthSegments: 64,
            depthSegments: 32
        }),
        material: createMaterial({
            ...SEA,
            tint: '#2a6fd6'
        }),
        castShadow: false,
        transform: {
            z: -1
        },
    });
    // A floor for the block's shadow to fall on.
    createMesh({
        geometry: usePlaneGeometry({
            width: 3,
            depth: 3
        }),
        tint: '#c9b48a',
        castShadow: false,
        transform: {
            x: -2.4,
            y: 0.25,
            z: 1.6
        }
    });

    const block = createMesh({
        geometry: useCubeGeometry(),
        material: createMaterial({
            ...BANDS,
            tint: '#ff9e3d',
            alphaCutoff: 0.5,
            cull: 'none'
        }),
        transform: {
            x: -2.4,
            y: 1.3,
            z: 1.6
        },
    });

    const crystal = createMesh({
        geometry: useIcoSphereGeometry({
            radius: 0.75,
            subdivisions: 1
        }),
        material: createMaterial({
            ...CRYSTAL,
            tint: '#8fe3ff',
            shininess: 64,
            specular: '#ffffff'
        }),
        transform: {
            x: 2.4,
            y: 1.2,
            z: 1.6
        },
    });

    useUpdate((delta) => {
        block.transform.rotationY += delta * 0.6;
        block.transform.rotationX += delta * 0.25;
        crystal.transform.rotationY -= delta * 0.3;
    });
};
```

`scenes/demo.ts`:

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

import { Exhibit } from '../components/exhibit';

export const Scene = () => {
    const camera = useCamera3d({
        projection: 'perspective',
        fov: 50,
        transform: {
            y: 3.2,
            z: 8.5
        }
    });

    cameraLookAt(camera, {
        x: 0,
        y: 0.6,
        z: 0
    });
    useDirectionalLight({
        color: '#fff3e0',
        intensity: 1,
        ambient: 0.3,
        castShadow: true,
        shadowArea: 14,
        transform: {
            rotationX: -0.85,
            rotationY: 0.6
        }
    });

    useSpawn(Exhibit)();
    createText({
        text: 'CARRY: CORNER TO PIXEL',
        transform: {
            x: 8,
            y: 8
        }
    });

    return createScene();
};
```

`settings.ts`:

```ts
export const WIDTH = 640;
export const HEIGHT = 360;
```

`shaders/carry.ts`:

```ts
import { compileShader, composerAdd, composerCarry, composerDot, composerFract, composerLight, composerMix, composerMul, composerShine, composerSin, composerSmoothstep, composerStep, composerSurface, composerSwizzle, composerTime, composerVec3, composerVec4, composerVertexPosition, composerX, composerY, composerZ } from 'nacatamalon/shader';

// The three shaders, each written once with the shader composer, so they draw on both cards.

// The engine's own look: the surface times the light, and the shine on top.
const lit = composerAdd(composerMul(composerSwizzle(composerSurface(), 'rgb'), composerLight()), composerShine());
const corner = composerVertexPosition();

// THE SEA. Two waves crossing lift each corner; how high is carried, and the colour turns to foam
// where it was highest. The lift is worked out once, for the corner and for carry both.
const lift = composerMul(
    composerAdd(
        composerSin(composerAdd(composerMul(composerX(corner), 1.3), composerTime())),
        composerSin(composerAdd(composerMul(composerZ(corner), 0.9), composerMul(composerTime(), 1.4))),
    ),
    0.18,
);

export const SEA = compileShader({
    shader: 'mesh3d',
    position: composerAdd(corner, composerVec3(0, lift, 0)),
    carry: lift,
    color: composerMix(lit, composerVec3(1, 1, 1), composerSmoothstep(0.2, 0.34, composerX(composerCarry()))),
});

// THE BLOCK. Each corner carries where it is in the block's own space; the colour keeps the bands
// where that runs one way and cuts the rest out. Turned, the bands go with it.
export const BANDS = compileShader({
    shader: 'mesh3d',
    carry: corner,
    color: composerVec4(lit, composerStep(0, composerSin(composerMul(composerY(composerCarry()), 16)))),
});

// THE CRYSTAL. Each corner carries a number of its own, made from where it is, and glints when the
// time comes round to its number: smeared across the facets, a glint runs over the stone.
const ownNumber = composerFract(composerMul(composerSin(composerDot(corner, composerVec3(12.9898, 78.233, 37.719))), 43758.5453));
const glint = composerSmoothstep(0.85, 1, composerSin(composerAdd(composerMul(composerTime(), 2.5), composerMul(composerX(composerCarry()), 6.283))));

export const CRYSTAL = compileShader({
    shader: 'mesh3d',
    carry: ownNumber,
    color: composerAdd(lit, composerMul(composerVec3(1, 1, 1), glint)),
});
```
