# Grassland

500,000 blades of grass built inside the draw call, which sway and receive shadows (StrandField/drawStrands; flip the parameter to see distance grading)

Run it with `swift run --package-path Examples Example-Rendering-Grassland`.

```swift
import Ollin

/// Half a million blades of grass with NO geometry anywhere. A `StrandField`
/// has no vertex buffer, no instance list, and nothing built in `setup()`: two
/// GPU stages grow every blade inside the draw call itself, from hashes of its
/// index. The camera runs the economy: tiles it cannot see are skipped whole,
/// and distant blades grow with fewer segments than near ones, which is the
/// kind of per-blade geometry change no instanced draw can express. The wind is
/// the sketch clock; the boulders are ordinary meshes whose cast shadows the
/// blades receive, because blades shade on the same lit path as every solid.
///
/// Flip the **detail** parameter off to give every blade full detail at every
/// distance, and watch the frame time: that gap is what distance-graded
/// geometry saves. The look barely moves, which is the point of putting the
/// detail where the camera can see it.
@main
final class Grassland: Sketch {

    @Param var detail = true   // off = full segments everywhere

    private var meadow = StrandField(width: 90, depth: 90, count: 500_000)

    override func setup() {
        meadow.bladeHeight = 0.7
        meadow.heightVariance = 0.45
        meadow.bladeWidth = 0.045
        meadow.lean = 0.34
        meadow.swayAmplitude = 0.14
        meadow.swayFrequency = 1.9
        meadow.lowColor = Color(hue: 0.31, saturation: 0.6, brightness: 0.18)
        meadow.tipColor = Color(hue: 0.24, saturation: 0.52, brightness: 0.66)
        meadow.detailNear = 10
        meadow.detailFar = 42
    }

    override func draw() {
        background(Color(hex: 0x11141B))
        meadow.isLevelOfDetailEnabled = detail

        let t = time * 0.04
        let eye = Vector3(cos(t * .tau) * 26, 2.6, sin(t * .tau) * 26)
        let ahead = Vector3(cos(t * .tau + 0.16) * 24, 0.9, sin(t * .tau + 0.16) * 24)
        camera(Camera3D(eye: eye, target: ahead, far: 80))

        ambientLight(Color(white: 0.15))
        directionalLight(Color(hue: 0.1, saturation: 0.25, brightness: 1.0),
                         direction: Vector3(-0.5, -0.75, -0.4), intensity: 1.0)
        directionalLight(Color(white: 0.4), direction: Vector3(0.55, 0.3, 0.5), intensity: 0.25,
                         castsShadow: false)   // a fill: one shadow pass, not two
        castShadows()
        fog(Color(hex: 0x11141B), density: 0.03)

        withState {
            fill(Color(hue: 0.29, saturation: 0.35, brightness: 0.16))
            specular(0.03)
            drawPlane(width: 200, depth: 200)
        }

        // A few boulders in the meadow: plain meshes, so they cast the shadows
        // the blades then receive.
        specular(0.1)
        specularSharpness(20)
        for i in 0 ..< 7 {
            withState {
                let a = Double(i) / 7 * .tau + 0.4
                let r = 8.0 + Double(i % 3) * 7.5
                translate(cos(a) * r, 0.55, sin(a) * r)
                rotateY(Double(i) * 1.3)
                scale(1 + Double(i % 3) * 0.6, 0.75, 1.2)
                fill(Color(hue: 0.09, saturation: 0.1, brightness: 0.5))
                drawSphere(radius: 0.9)
            }
        }

        drawStrands(meadow)

        drawCaption("Strands: 500,000 blades grown inside the draw call")
    }
}
```
