tiramisu/scene

Scene graph module - declarative 3D scene construction.

This module provides types and functions for building 3D scenes declaratively. Scenes are composed of SceneNode values that describe meshes, lights, cameras, and groups.

Core Concepts

Quick Example

import tiramisu/scene
import tiramisu/transform
import gleam/option
import vec/vec3

pub fn view(model: Model) {
  let assert Ok(geometry) = scene.box(width: 1.0, height: 1.0, depth: 1.0)
  let assert Ok(material) = scene.basic_material(color: 0xff0000, transparent: False, opacity: 1.0)

  [
    scene.Mesh(
      id: "player",
      geometry: geometry,
      material: material,
      transform: transform.at(vec3.Vec3(0.0, 1.0, 0.0)),
      physics: option.None,
    ),
    scene.Light(
      id: "sun",
      light_type: scene.DirectionalLight(color: 0xffffff, intensity: 1.0),
      transform: transform.identity,
    ),
  ]
}

Types

Opaque type for Three.js BufferGeometry.

Created by loading 3D models with asset.load_stl() or asset.load_model().

pub type BufferGeometry

3D geometry types supported by the engine.

Each variant represents a different primitive shape or custom geometry. Use the validated constructor functions like box(), sphere(), etc.

pub type GeometryType {
  BoxGeometry(width: Float, height: Float, depth: Float)
  SphereGeometry(
    radius: Float,
    width_segments: Int,
    height_segments: Int,
  )
  ConeGeometry(radius: Float, height: Float, segments: Int)
  PlaneGeometry(width: Float, height: Float)
  CircleGeometry(radius: Float, segments: Int)
  CylinderGeometry(
    radius_top: Float,
    radius_bottom: Float,
    height: Float,
    radial_segments: Int,
  )
  TorusGeometry(
    radius: Float,
    tube: Float,
    radial_segments: Int,
    tubular_segments: Int,
  )
  TetrahedronGeometry(radius: Float, detail: Int)
  IcosahedronGeometry(radius: Float, detail: Int)
  CustomGeometry(BufferGeometry)
}

Constructors

  • BoxGeometry(width: Float, height: Float, depth: Float)
  • SphereGeometry(
      radius: Float,
      width_segments: Int,
      height_segments: Int,
    )
  • ConeGeometry(radius: Float, height: Float, segments: Int)
  • PlaneGeometry(width: Float, height: Float)
  • CircleGeometry(radius: Float, segments: Int)
  • CylinderGeometry(
      radius_top: Float,
      radius_bottom: Float,
      height: Float,
      radial_segments: Int,
    )
  • TorusGeometry(
      radius: Float,
      tube: Float,
      radial_segments: Int,
      tubular_segments: Int,
    )
  • TetrahedronGeometry(radius: Float, detail: Int)
  • IcosahedronGeometry(radius: Float, detail: Int)
  • CustomGeometry(BufferGeometry)

Level of Detail (LOD) configuration.

Defines which mesh to display based on camera distance. Use with LOD scene node for automatic detail switching to improve performance.

Example

scene.LOD(
  id: "tree",
  levels: [
    scene.lod_level(distance: 0.0, node: high_detail_mesh),   // 0-50 units
    scene.lod_level(distance: 50.0, node: medium_detail_mesh), // 50-100 units
    scene.lod_level(distance: 100.0, node: low_detail_mesh),   // 100+ units
  ],
  transform: transform.identity,
)
pub type LODLevel {
  LODLevel(distance: Float, node: SceneNode)
}

Constructors

Light types for illuminating the scene.

Different lights have different performance impacts and visual characteristics. Most games use a combination of ambient + directional for outdoor scenes, or ambient + point/spot for indoor scenes.

pub type LightType {
  AmbientLight(color: Int, intensity: Float)
  DirectionalLight(color: Int, intensity: Float)
  PointLight(color: Int, intensity: Float, distance: Float)
  SpotLight(
    color: Int,
    intensity: Float,
    distance: Float,
    angle: Float,
    penumbra: Float,
  )
  HemisphereLight(
    sky_color: Int,
    ground_color: Int,
    intensity: Float,
  )
}

Constructors

  • AmbientLight(color: Int, intensity: Float)

    Global ambient light (affects all objects equally, no direction).

  • DirectionalLight(color: Int, intensity: Float)

    Directional light like the sun (parallel rays, infinite distance).

  • PointLight(color: Int, intensity: Float, distance: Float)

    Point light that radiates in all directions (like a light bulb).

  • SpotLight(
      color: Int,
      intensity: Float,
      distance: Float,
      angle: Float,
      penumbra: Float,
    )

    Cone-shaped spotlight (like a flashlight or stage light).

  • HemisphereLight(
      sky_color: Int,
      ground_color: Int,
      intensity: Float,
    )

    Hemisphere light with different colors for sky and ground (outdoor ambient).

Material types for rendering objects.

Materials define how surfaces appear when rendered. Different materials have different performance characteristics and visual properties.

Performance

  • BasicMaterial: Fastest, no lighting calculations
  • LambertMaterial, ToonMaterial: Fast, simple lighting
  • PhongMaterial: Medium, specular highlights
  • StandardMaterial: Physically-based, most realistic but slower
pub type MaterialType {
  BasicMaterial(
    color: Int,
    transparent: Bool,
    opacity: Float,
    map: option.Option(Texture),
  )
  StandardMaterial(
    color: Int,
    metalness: Float,
    roughness: Float,
    map: option.Option(Texture),
    normal_map: option.Option(Texture),
  )
  PhongMaterial(
    color: Int,
    shininess: Float,
    map: option.Option(Texture),
  )
  LambertMaterial(color: Int, map: option.Option(Texture))
  ToonMaterial(color: Int, map: option.Option(Texture))
  LineMaterial(color: Int, linewidth: Float)
  SpriteMaterial(
    color: Int,
    transparent: Bool,
    opacity: Float,
    map: option.Option(Texture),
  )
}

Constructors

  • BasicMaterial(
      color: Int,
      transparent: Bool,
      opacity: Float,
      map: option.Option(Texture),
    )

    Unlit material (no lighting calculations). Fast and useful for flat-shaded objects.

  • StandardMaterial(
      color: Int,
      metalness: Float,
      roughness: Float,
      map: option.Option(Texture),
      normal_map: option.Option(Texture),
    )

    Physically-based material with metalness/roughness workflow. Most realistic.

  • PhongMaterial(
      color: Int,
      shininess: Float,
      map: option.Option(Texture),
    )

    Shiny material with specular highlights (like plastic or ceramic).

  • LambertMaterial(color: Int, map: option.Option(Texture))

    Matte material (like cloth or wood). Non-shiny diffuse lighting.

  • ToonMaterial(color: Int, map: option.Option(Texture))

    Cartoon-style material with banded shading.

  • LineMaterial(color: Int, linewidth: Float)

    Material for rendering lines.

  • SpriteMaterial(
      color: Int,
      transparent: Bool,
      opacity: Float,
      map: option.Option(Texture),
    )

    Material for 2D sprites that always face the camera.

Scene node - the core building block of your 3D scene.

Scene nodes are immutable, declarative descriptions of objects in your game. Each frame, your view() function returns a list of scene nodes, and the engine efficiently updates only what changed.

Node Types

  • Mesh: Standard 3D object (1 draw call per mesh)
  • InstancedMesh: Many identical objects (1 draw call total!)
  • Group: Container for organizing child nodes in a hierarchy
  • Light: Illuminates the scene
  • Camera: Defines viewpoint (must have at least one with active: True)
  • LOD: Switches detail levels based on distance
  • Model3D: Loaded 3D model with animations
  • Audio: Background or positional audio
  • Debug*: Visualization helpers for development

Example

pub fn view(model: Model) {
  [
    scene.Group(
      id: "player",
      transform: transform.at(model.position),
      children: [
        scene.Mesh(
          id: "player-body",
          geometry: scene.BoxGeometry(1.0, 2.0, 1.0),
          material: scene.BasicMaterial(0x00ff00, False, 1.0, option.None),
          transform: transform.identity,
          physics: option.Some(model.physics_body),
        ),
      ],
    ),
  ]
}
pub type SceneNode {
  Mesh(
    id: String,
    geometry: GeometryType,
    material: MaterialType,
    transform: transform.Transform,
    physics: option.Option(physics.RigidBody),
  )
  InstancedMesh(
    id: String,
    geometry: GeometryType,
    material: MaterialType,
    instances: List(transform.Transform),
  )
  Group(
    id: String,
    transform: transform.Transform,
    children: List(SceneNode),
  )
  Light(
    id: String,
    light_type: LightType,
    transform: transform.Transform,
  )
  Camera(
    id: String,
    camera: camera.Camera,
    transform: transform.Transform,
    active: Bool,
    viewport: option.Option(#(Int, Int, Int, Int)),
  )
  LOD(
    id: String,
    levels: List(LODLevel),
    transform: transform.Transform,
  )
  Model3D(
    id: String,
    object: object3d.Object3D,
    transform: transform.Transform,
    animation: option.Option(object3d.AnimationPlayback),
    physics: option.Option(physics.RigidBody),
  )
  Audio(
    id: String,
    buffer: audio.AudioBuffer,
    config: audio.AudioConfig,
    audio_type: audio.AudioType,
  )
  DebugBox(
    id: String,
    min: vec3.Vec3(Float),
    max: vec3.Vec3(Float),
    color: Int,
  )
  DebugSphere(
    id: String,
    center: vec3.Vec3(Float),
    radius: Float,
    color: Int,
  )
  DebugLine(
    id: String,
    from: vec3.Vec3(Float),
    to: vec3.Vec3(Float),
    color: Int,
  )
  DebugAxes(id: String, origin: vec3.Vec3(Float), size: Float)
  DebugGrid(id: String, size: Float, divisions: Int, color: Int)
  DebugPoint(
    id: String,
    position: vec3.Vec3(Float),
    size: Float,
    color: Int,
  )
}

Constructors

Opaque type for Three.js textures.

Created via asset.load_texture() and used in materials.

pub type Texture

Validation errors returned by geometry and material constructors.

These errors help catch invalid parameters at creation time instead of runtime.

pub type ValidationError {
  InvalidDimension(String, Float)
  InvalidSegmentCount(String, Int)
  InvalidOpacity(Float)
  InvalidMetalness(Float)
  InvalidRoughness(Float)
  InvalidIntensity(Float)
  InvalidLinewidth(Float)
}

Constructors

  • InvalidDimension(String, Float)

    Dimension parameter (width, height, radius, etc.) is invalid (must be > 0)

  • InvalidSegmentCount(String, Int)

    Segment count parameter is invalid (typically must be >= 3)

  • InvalidOpacity(Float)

    Opacity must be between 0.0 and 1.0

  • InvalidMetalness(Float)

    Metalness must be between 0.0 and 1.0

  • InvalidRoughness(Float)

    Roughness must be between 0.0 and 1.0

  • InvalidIntensity(Float)

    Light intensity must be positive

  • InvalidLinewidth(Float)

    Line width must be positive

Values

pub fn basic_material(
  color color: Int,
  transparent transparent: Bool,
  opacity opacity: Float,
) -> Result(MaterialType, ValidationError)

Create a validated basic (unlit) material.

Basic materials don’t react to lights, making them very fast to render. Opacity must be between 0.0 (fully transparent) and 1.0 (fully opaque).

Example

let assert Ok(red) = scene.basic_material(color: 0xff0000, transparent: False, opacity: 1.0)
let assert Ok(glass) = scene.basic_material(color: 0x88ccff, transparent: True, opacity: 0.5)
pub fn box(
  width width: Float,
  height height: Float,
  depth depth: Float,
) -> Result(GeometryType, ValidationError)

Create a validated box geometry.

All dimensions must be positive (> 0).

Example

let assert Ok(cube) = scene.box(width: 1.0, height: 1.0, depth: 1.0)
let assert Ok(wall) = scene.box(width: 10.0, height: 3.0, depth: 0.1)
pub fn cylinder(
  radius_top radius_top: Float,
  radius_bottom radius_bottom: Float,
  height height: Float,
  radial_segments radial_segments: Int,
) -> Result(GeometryType, ValidationError)

Create a validated cylinder geometry.

Both radii must be non-negative, height positive, radial segments >= 3. Set one radius to 0 to create a cone shape.

Example

let assert Ok(cylinder) = scene.cylinder(radius_top: 1.0, radius_bottom: 1.0, height: 2.0, radial_segments: 32)
let assert Ok(cone) = scene.cylinder(radius_top: 0.0, radius_bottom: 1.0, height: 2.0, radial_segments: 32)
pub fn icosahedron(
  radius radius: Float,
  detail detail: Int,
) -> Result(GeometryType, ValidationError)

Create a validated icosahedron (20-sided polyhedron) geometry.

Detail level controls subdivision. Good for creating spheres with flat faces.

Example

let assert Ok(shape) = scene.icosahedron(radius: 1.0, detail: 2)
pub fn line_material(
  color color: Int,
  linewidth linewidth: Float,
) -> Result(MaterialType, ValidationError)

Create a validated line material for rendering lines.

Example

let assert Ok(line_mat) = scene.line_material(color: 0xff0000, linewidth: 2.0)
pub fn lod_level(
  distance distance: Float,
  node node: SceneNode,
) -> LODLevel

Create an LOD level with a distance threshold and scene node.

Levels should be ordered from closest (distance: 0.0) to farthest.

Example

let high_detail = scene.lod_level(distance: 0.0, node: detailed_mesh)
let low_detail = scene.lod_level(distance: 100.0, node: simple_mesh)
pub fn sphere(
  radius radius: Float,
  width_segments width_segments: Int,
  height_segments height_segments: Int,
) -> Result(GeometryType, ValidationError)

Create a validated sphere geometry.

Radius must be positive. Width segments >= 3, height segments >= 2. More segments = smoother sphere but more triangles.

Example

let assert Ok(ball) = scene.sphere(radius: 1.0, width_segments: 32, height_segments: 16)
let assert Ok(low_poly) = scene.sphere(radius: 1.0, width_segments: 8, height_segments: 6)
pub fn sprite_material(
  color color: Int,
  transparent transparent: Bool,
  opacity opacity: Float,
) -> Result(MaterialType, ValidationError)

Create a validated sprite material for 2D billboards.

Sprites always face the camera and are useful for particles, UI elements, etc.

Example

let assert Ok(sprite_mat) = scene.sprite_material(color: 0xffffff, transparent: True, opacity: 0.8)
pub fn standard_material(
  color color: Int,
  metalness metalness: Float,
  roughness roughness: Float,
) -> Result(MaterialType, ValidationError)

Create a validated physically-based (PBR) standard material.

Standard materials use metalness/roughness workflow for realistic rendering.

  • Metalness: 0.0 = dielectric (plastic, wood), 1.0 = metal
  • Roughness: 0.0 = mirror-smooth, 1.0 = completely rough

Example

let assert Ok(gold) = scene.standard_material(color: 0xffd700, metalness: 1.0, roughness: 0.3)
let assert Ok(plastic) = scene.standard_material(color: 0xff0000, metalness: 0.0, roughness: 0.5)
pub fn tetrahedron(
  radius radius: Float,
  detail detail: Int,
) -> Result(GeometryType, ValidationError)

Create a validated tetrahedron (4-sided polyhedron) geometry.

Detail level controls subdivision (0 = no subdivision, higher = more triangles).

Example

let assert Ok(shape) = scene.tetrahedron(radius: 1.0, detail: 0)
pub fn torus(
  radius radius: Float,
  tube tube: Float,
  radial_segments radial_segments: Int,
  tubular_segments tubular_segments: Int,
) -> Result(GeometryType, ValidationError)

Create a validated torus (donut) geometry.

Example

let assert Ok(donut) = scene.torus(radius: 2.0, tube: 0.5, radial_segments: 16, tubular_segments: 100)
Search Document