# RaymarchedCastShadow

A field casting a shadow onto meshes under a directional or point light (any key switches). Either the field renders into the 2D shadow map, or the lit mesh fragments march the field inline toward the light.

Run it with `swift run --package-path Examples Example-3D-Raymarching-RaymarchedCastShadow`.

```swift
import Ollin
import Foundation

// A raymarched 3D SDF field casting a shadow onto a rasterized mesh, under either kind of
// caster (press any key to switch). The field self-shadows analytically, but under the
// directional light it also renders into the 2D shadow map, so meshes receive its cast
// shadow just as they do another mesh's. A point light has no such map, so the lit mesh
// fragments march the field inline toward the light instead. Either way the mesh floor
// (and the mesh sphere on the right, for comparison) catch the soft shadow of the
// floating field blob on the left.
@main
final class RaymarchedCastShadow: Sketch {

    /// Press any key to swap the caster kind.
    var usesPointLight = false

    override func draw() {
        background(Color(hex: 0x0c0f16))
        let t = time

        if usesPointLight {
            // The point variant lifts the camera and steps it back, so the overhead
            // bulb's shadows read on the floor.
            cameraShowcase(.turntable(period: .tau / 0.2), target: Vector3(0, 0.8, 0), radius: 11,
                        elevation: 0.5, fieldOfView: .pi / 4.6)
            pointLight(.white, at: Vector3(0, 5.5, 0), intensity: 1.8, specular: .white)
        } else {
            cameraShowcase(.turntable(period: .tau / 0.25), target: .zero, radius: 7.5,
                        elevation: 0.4, fieldOfView: .pi / 4, near: 0.1, far: 50)
            directionalLight(.white, direction: Vector3(0.35, -0.92, -0.2),
                             intensity: 1.3, softness: 0.2)
        }
        ambientLight(Color(white: 0.16))
        castShadows()
        material(.glossy)

        // Rasterized meshes: the floor (the receiver) and a sphere whose own cast shadow is the
        // reference the field's should match.
        fill(Color(hex: 0x5b6472))
        withState { translate(0, -1.1, 0); drawBox(width: 9, height: 0.4, depth: 9) }
        fill(Color(hex: 0xf472b6))
        withState { translate(2.1, -0.3, 0); drawSphere(radius: 0.6) }

        // A raymarched SDF blob floating above the floor. Under the directional light it
        // drops its soft shadow onto the mesh floor through the field-into-shadow-map pass,
        // the same way the mesh sphere does; under the point light, through the mesh
        // fragments' inline field march (the point-light cast path).
        let blob = SDF3D.sphere(radius: 0.7)
            .smoothUnion(SDF3D.sphere(radius: 0.5).at(0.85, 0.35, 0.2), k: 0.45)
            .smoothUnion(SDF3D.sphere(radius: 0.5).at(-0.2, 0.5, -0.4), k: 0.45)
            .colored(Color(hex: 0x38bdf8))
        withState { translate(-1.8, 0.1, 0); rotateY(t * 0.4); drawSDF3D(blob) }

        drawCaption(usesPointLight
            ? "Field casting onto a mesh under a point light (any key: directional)"
            : "Field casting onto a mesh under a directional light (any key: point)")
    }

    override func keyPressed() { usesPointLight.toggle() }
}
```
