tiramisu/model

3D Model loading and manipulation.

This module provides functions for loading 3D models in various formats (GLTF, OBJ, FBX) and extracting their components like scenes and animations.

Supported Formats

Example

import tiramisu/model
import gleam/javascript/promise

// Load a GLTF model
use result <- promise.await(model.load_gltf("/models/character.glb"))
case result {
  Ok(gltf) -> {
    // Get the scene (root Object3D)
    let scene = model.get_scene(gltf)

    // Get animations if any
    let clips = model.get_animations(gltf)
  }
  Error(Nil) -> io.println("Failed to load model")
}

Types

Configuration for a single animation clip.

Use new_animation() to create an animation with defaults, then use the builder functions (set_loop(), set_speed(), etc.) to configure it.

pub type Animation {
  Animation(
    clip: savoiardi.AnimationClip,
    loop: LoopMode,
    speed: Float,
    weight: Float,
  )
}

Constructors

  • Animation(
      clip: savoiardi.AnimationClip,
      loop: LoopMode,
      speed: Float,
      weight: Float,
    )

    Arguments

    speed

    Playback speed multiplier (1.0 = normal, 2.0 = double speed, 0.5 = half speed)

    weight

    Animation weight for blending (0.0 to 1.0, where 1.0 = full influence)

Animation playback configuration for a Model scene node.

You can play a single animation or blend between two animations for smooth transitions.

pub type AnimationPlayback {
  SingleAnimation(Animation)
  BlendedAnimations(
    from: Animation,
    to: Animation,
    blend_factor: Float,
  )
}

Constructors

  • SingleAnimation(Animation)

    Play a single animation

  • BlendedAnimations(
      from: Animation,
      to: Animation,
      blend_factor: Float,
    )

    Blend between two animations with a blend factor (0.0 = fully ‘from’, 1.0 = fully ‘to’)

Loaded FBX model data.

Contains the scene graph and animations. FBX is commonly used in game development tools like Unity and Unreal Engine.

pub type FBXData =
  savoiardi.FBXData

Loaded GLTF/GLB model data.

Contains the scene graph, animations, cameras, and other assets. GLTF is the recommended format for 3D models on the web.

pub type GLTFData =
  savoiardi.GLTFData

Controls how an animation loops.

pub type LoopMode {
  LoopOnce
  LoopRepeat
}

Constructors

  • LoopOnce

    Play the animation once and stop at the end

  • LoopRepeat

    Play the animation repeatedly in a loop

Values

pub fn apply_texture(
  object: savoiardi.Object3D,
  tex: savoiardi.Texture,
  filter_mode: texture.FilterMode,
) -> Nil

Apply a texture to all meshes in an Object3D hierarchy.

This is useful for loaded models that reference external textures, or when you want to override the model’s textures.

Example

let floor = model.get_fbx_scene(loaded_fbx)
model.apply_texture(floor, dungeon_texture, texture.NearestFilter)
pub fn center_object(
  object: savoiardi.Object3D,
) -> savoiardi.Object3D

Center an Object3D so its geometric center is at the origin.

Computes the bounding box of the entire object hierarchy and adjusts all children’s positions so the center of the bounding box is at (0, 0, 0).

This is useful for loaded models (FBX, GLTF, OBJ) where the origin may not be at the geometric center of the mesh.

Example

let fbx_data = model.get_fbx_scene(loaded_fbx)
let centered = model.center_object(fbx_data)
// Now the model's center is at (0, 0, 0)

Notes

  • This mutates the object in place and returns it for convenience
  • The children’s positions are adjusted, not the root object’s position
pub fn clip_duration(
  clip: savoiardi.AnimationClip,
) -> duration.Duration

Get the duration of an animation clip.

The duration is in seconds.

Example

let anim = model.new_animation(walk_clip)
let duration = model.clip_duration(walk_clip)
// Use this to sync game events with animation timing
pub fn clip_name(clip: savoiardi.AnimationClip) -> String

Get the name of an animation clip.

Useful for finding specific animations by name when loading from GLTF files.

Example

import gleam/list
import tiramisu/model

// After loading a GLTF model
let clips = model.get_animations(gltf_data)

let walk_clip = list.find(clips, fn(clip) {
  model.clip_name(clip) == "Walk"
})
pub fn get_animations(
  gltf: savoiardi.GLTFData,
) -> List(savoiardi.AnimationClip)

Get all animation clips from loaded GLTF data.

Returns an empty list if the model has no animations.

Example

let clips = model.get_animations(gltf_data)
// Use clips with model.new_animation() and scene.object_3d()
pub fn get_cameras(
  gltf: savoiardi.GLTFData,
) -> List(savoiardi.Object3D)

Get all cameras from loaded GLTF data.

GLTF files can include camera definitions. Returns an empty list if the model has no cameras.

pub fn get_fbx_animations(
  fbx: savoiardi.FBXData,
) -> List(savoiardi.AnimationClip)

Get all animation clips from loaded FBX data.

Returns an empty list if the model has no animations.

Example

let clips = model.get_fbx_animations(fbx_data)
// Use clips with model.new_animation() and scene.object_3d()
pub fn get_fbx_scene(
  fbx: savoiardi.FBXData,
) -> savoiardi.Object3D

Get the root scene (Object3D) from loaded FBX data.

FBX data is itself an Object3D (THREE.Group), so this just returns it.

Example

let scene = model.get_fbx_scene(fbx_data)
// Use with scene.model() node
pub fn get_scene(gltf: savoiardi.GLTFData) -> savoiardi.Object3D

Get the root scene (Object3D) from loaded GLTF data.

This is the main 3D object hierarchy that you can add to your scene.

Example

let scene = model.get_scene(gltf_data)
// Use with scene.model() node
pub fn load_fbx(
  from url: String,
  on_success on_success: fn(savoiardi.FBXData) -> msg,
  on_error on_error: msg,
) -> effect.Effect(msg)
pub fn load_gltf(
  from url: String,
  on_success on_success: fn(savoiardi.GLTFData) -> msg,
  on_error on_error: msg,
) -> effect.Effect(msg)
pub fn load_obj(
  from url: String,
  on_success on_success: fn(savoiardi.Object3D) -> msg,
  on_error on_error: msg,
) -> effect.Effect(msg)
pub fn new_animation(clip: savoiardi.AnimationClip) -> Animation

Create an animation from a clip with default settings.

Defaults: loop repeat, normal speed (1.0x), full weight (1.0).

Example

import tiramisu/model

// After loading a GLTF model
let clips = model.get_animations(gltf_data)
let walk_clip = list.find(clips, fn(clip) { model.clip_name(clip) == "Walk" })

let walk_animation = model.new_animation(walk_clip)
pub fn set_loop(anim: Animation, mode: LoopMode) -> Animation

Set the loop mode for an animation.

Example

let jump_animation = model.new_animation(jump_clip)
  |> model.set_loop(model.LoopOnce)  // Play once, don't loop
pub fn set_speed(anim: Animation, speed: Float) -> Animation

Set the playback speed multiplier.

The speed multiplier affects how fast the animation plays:

  • 1.0 = normal speed
  • 2.0 = double speed (twice as fast)
  • 0.5 = half speed (slow motion)
  • Negative values play the animation in reverse

Example

let run_animation = model.new_animation(run_clip)
  |> model.set_speed(1.5)  // 50% faster running
pub fn set_weight(anim: Animation, weight: Float) -> Animation

Set the animation weight for blending.

The weight value ranges from 0.0 to 1.0:

  • 1.0 = full influence (default)
  • 0.5 = half influence (useful for blending)
  • 0.0 = no influence (animation has no effect)

Example

// Blend two animations manually
let walk = model.new_animation(walk_clip) |> model.set_weight(0.7)
let idle = model.new_animation(idle_clip) |> model.set_weight(0.3)

// Or use BlendedAnimations with a blend factor
model.BlendedAnimations(from: walk, to: idle, blend_factor: 0.5)
Search Document