# `CauldronGPU.AliasModel`
[🔗](https://github.com/jaman/cauldron/blob/v0.3.3/cauldron_gpu/lib/cauldron_gpu/alias_model.ex#L2)

A Quake alias model (a `Cauldron3D.Mesh` posed by keyframes, as read by
`Cauldron3D.Formats.Mdl`) uploaded for drawing: one mesh per keyframe pose, every one
with the same vertices in the same order and the same indices, so a
`CauldronGPU.Pose` blends between any two; each skin as a base texture and, when it
has palette colours 224 to 254, a glow texture holding only those. Skin textures have
no mipmaps: every skin is read at full size, as Quake reads it.

    bytes = Files.read(files, "progs/soldier.mdl")
    {:ok, mdl} = Cauldron3D.Formats.Mdl.parse(bytes)
    model = CauldronGPU.AliasModel.upload(gpu, mdl, palette, names: CauldronGPU.AliasModel.frame_names(bytes))
    material = CauldronGPU.AliasModel.material(gpu, model)
    instance = CauldronGPU.AliasModel.instance(model, [material], CauldronGPU.Placement.transform(1 / 32, origin, 90))
    CauldronGPU.submit(gpu, CauldronGPU.AliasModel.animate(model, 7, instance, "stand", 1.25))

Vertices stay in the model's own units and axes (x forward, y left, z up);
`CauldronGPU.Placement.transform/3` stands a model at a map point.

An animation is the poses whose names share a stem once trailing digits are taken
off (`stand1` … `stand8` is `"stand"`), in pose order. `frame/3` plays the model's
frame numbers as the game numbers them, a frame group cycling by its own intervals.

# `sample`

```elixir
@type sample() :: {non_neg_integer(), non_neg_integer(), float()}
```

# `skin`

```elixir
@type skin() :: %{base: CauldronGPU.Handle.t(), glow: CauldronGPU.Handle.t() | nil}
```

# `t`

```elixir
@type t() :: %CauldronGPU.AliasModel{
  animations: %{required(String.t()) =&gt; [non_neg_integer()]},
  flags: non_neg_integer(),
  frames: tuple(),
  names: tuple(),
  poses: tuple(),
  skins: [skin()]
}
```

# `animate`

```elixir
@spec animate(
  t(),
  CauldronGPU.Stream.id(),
  CauldronGPU.Instance.t(),
  String.t(),
  number(),
  keyword()
) ::
  [CauldronGPU.Stream.change()]
```

`pose/4` for `animation` at `time` (options as `sample/4`); an unknown animation shows pose 0.

# `animations`

```elixir
@spec animations(t()) :: [String.t()]
```

The animation names, sorted.

# `frame`

```elixir
@spec frame(t(), integer(), number()) :: sample()
```

The two poses `{from, to, blend}` of the game's frame number `frame` (out of range is
frame 0) at `time` seconds: a single frame is its pose unblended, a frame group moves
through its poses by their intervals.

# `frame_names`

```elixir
@spec frame_names(binary()) :: [String.t()]
```

The name of every pose in the `.mdl` file `bytes`, in pose order (a frame group's poses each have one).

# `handles`

```elixir
@spec handles(t()) :: [CauldronGPU.Handle.t()]
```

Every handle the model holds.

# `instance`

```elixir
@spec instance(t(), [CauldronGPU.Handle.t()], CauldronGPU.Transform.t()) ::
  CauldronGPU.Instance.t()
```

An instance of the model's first pose with `materials` at `transform`.

# `material`

```elixir
@spec material(CauldronGPU.Target.t(), t(), keyword()) :: CauldronGPU.Handle.t()
```

A material drawing skin `:skin` (default 0). Options: `:lightmap`, a texture whose
texel at lightmap coordinates `{0, 0}` lights the whole model (a 1×1 texture of the
light where it stands), times `:light_scale` (default 2.0).

# `pose`

```elixir
@spec pose(t(), CauldronGPU.Stream.id(), CauldronGPU.Instance.t(), sample()) :: [
  CauldronGPU.Stream.change()
]
```

The changes showing instance `id` (as `instance`, at its transform) in `sample`: the
instance on pose `from`, then its `{:pose, …}` blending `blend` of the way to `to`.

# `rotates?`

```elixir
@spec rotates?(t()) :: boolean()
```

Whether the game spins the model about its up axis (model flag 8, pickups).

# `sample`

```elixir
@spec sample(t(), String.t(), number(), keyword()) ::
  {:ok, sample()} | {:error, :unknown_animation}
```

The two poses `{from, to, blend}` of `animation` at `time` seconds. Options: `:fps`,
poses a second (default 10), and `:loop` (default `true`); an animation that does not
loop holds its last pose. Returns `{:error, :unknown_animation}` for a name the model
lacks.

# `upload`

```elixir
@spec upload(CauldronGPU.Target.t(), map(), binary(), keyword()) :: t()
```

Uploads `mdl` (a map with `:mesh`, a `Cauldron3D.Mesh`, and the game's frame table
`:frames`, such as a `Cauldron3D.Formats.Mdl`). `palette` is 768 bytes.

Options: `:names`, one name per pose (`frame_names/1`; without them there are no
animations), and `:filter`, `:nearest` (default) or `:linear`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
