# `Cauldron3D.Formats.Studio`
[🔗](https://github.com/jaman/cauldron/blob/v0.2.2/cauldron_3d_formats/lib/cauldron_3d/formats/studio.ex#L1)

A studio model, `.mdl` version 10: bones, bone controllers, hitboxes, sequences with
their events, attachments, textures with their own palettes, skin families, and body
parts of skinned triangle meshes, with the compressed animation decoded on demand.

    {:ok, studio} = Cauldron3D.Formats.Studio.parse(bytes)
    studio = Cauldron3D.Formats.Studio.add_textures(studio, File.read!("scientistt.mdl"))
    studio = Cauldron3D.Formats.Studio.add_group(studio, 1, File.read!("scientist01.mdl"))
    locals = Cauldron3D.Formats.Studio.locals(studio, 3, 12.5)
    transforms = Cauldron3D.Skeleton.pose(studio.parents, locals)

`locals/4` gives each bone's position and rotation relative to its parent for a frame
of a sequence; `Cauldron3D.Skeleton.pose/3` turns them into transforms, and
`vertices/3` moves a body part's vertices by them.

A model whose textures are in a separate `…t.mdl` holds none until `add_textures/2`;
a sequence in group `n` above 0 has its animation in a `…0n.mdl` file that
`add_group/3` supplies.

# `t`

```elixir
@type t() :: %Cauldron3D.Formats.Studio{
  animation: term(),
  attachments: term(),
  body_parts: term(),
  bones: term(),
  box_maxs: term(),
  box_mins: term(),
  controllers: term(),
  eye: term(),
  flags: term(),
  groups: term(),
  hitboxes: term(),
  maxs: term(),
  mins: term(),
  name: term(),
  parents: term(),
  sequences: term(),
  skins: term(),
  texture_flags: term(),
  textures: term()
}
```

# `add_group`

```elixir
@spec add_group(t(), pos_integer(), binary()) :: t()
```

The model with the animation of sequence group `group` from its file `data` (`…0n.mdl`).

# `add_textures`

```elixir
@spec add_textures(t(), binary()) :: t()
```

The model with the textures and skins of its separate texture file `data` (`…t.mdl`).

# `body_model`

```elixir
@spec body_model(t(), non_neg_integer(), non_neg_integer()) :: non_neg_integer()
```

The model index within body part `part` that body value `body` selects.

# `locals`

```elixir
@spec locals(t(), integer(), number(), keyword()) :: [
  {Cauldron3D.Vec.t(), Cauldron3D.Quat.t()}
]
```

Each bone's position and rotation relative to its parent for sequence `index` at
`frame`, a float counting frames from the sequence's start (seconds times its frame
rate). A looping sequence wraps every `frames` frames; one that does not loop holds
its last frame. Between two frames the rotations are slerped and the positions
mixed. A sequence index outside the model is sequence 0.

## Options

  * `:bonecontrols` — the five bone controller values in the controllers' own units
    (degrees for a turning controller), the fifth the mouth; each is held inside its
    controller's range unless the controller wraps (type bit `0x8000`), and a turning
    controller's value (types `0x08` to `0x20`) is taken as degrees
  * `:controllers` — up to four settings 0 to 255 instead, each standing for
    `start + n / 255 * (end - start)`, and `:mouth` the fifth likewise
  * `:subblend` — `{first, second}`, each -1.0 to 1.0, choosing among a sequence's
    blends along its first and second blend axes, default `{0.0, 0.0}`
  * `:blend` — 0 to 255 instead of `:subblend`'s first value, standing for
    `n / 255 * 2 - 1`
  * `:base` — `{sequence, frame, last}`: bones before bone `last` take sequence
    `sequence` at `frame` instead

# `missing_groups`

```elixir
@spec missing_groups(t()) :: [pos_integer()]
```

The groups above 0 whose animation `add_group/3` has not yet supplied.

# `parse`

```elixir
@spec parse(binary()) :: {:ok, t()} | {:error, :not_a_studio_model}
```

The model in `data`.

# `polygons`

```elixir
@spec polygons(t(), tuple(), keyword()) :: [Cauldron3D.Polygon.t()]
```

The model's triangles as `Cauldron3D.Polygon`s, posed by `transforms` (one per bone,
from `Cauldron3D.Skeleton.pose/3`, the entity's place and turn included).

Each body part draws the model its geomset value names, counting from 1, or its first
model when the value is 0 or missing. A mesh draws the texture its skin reference
names in skin family `:skin`, or in family 0 when the model has no such family; a mesh
whose reference is beyond the model's references is not drawn.

Each corner is lit by its normal, turned by its vertex's bone: `ambient + diffuse *
(normal · direction / 2 + 1 / 2)` a channel, or `ambient + diffuse * (normal ·
direction)` without half-Lambert; with `:ldr` each channel is then held at 1.5 or
below, cut down to steps of 1/32 (red and blue) or 1/64 (green) and multiplied by
0.75. The light times `:colour`'s red, green and blue, and `:colour`'s alpha, are the
corner's tint.

A texture flagged masked (`0x40`) is see-through at colour 255, and one flagged masked
or alpha-solid (`0x800`) and not fullbright (`0x04`) is cut out where its alpha is
below half. A texture flagged chrome (`0x02`) takes its place on the texture from the
normal and the eye: for each vertex, with `toward` the direction from `:eye` to the
vertex's bone, `up` the normalised `toward × :view_right` and `right` the normalised
`up × toward`, both turned back into the bone's own space, the place is
`((normal · right + 1) / 2, (normal · up + 1) / 2)` of the texture's size.

## Options

  * `:geomsets` — a map from body part to its geomset value, default `%{}`
  * `:skin` — the skin family, default 0
  * `:light` — `%{ambient: {r, g, b}, diffuse: {r, g, b}, direction: vec}` (1.0 a
    texture's own colours, `direction` in the world, of length one), or one number
    for an even light (1.0 the texture's own colours); default 1.0
  * `:halflambert` — default true
  * `:ldr` — default false
  * `:colour` — `{r, g, b, a}`, default `{1.0, 1.0, 1.0, 1.0}`
  * `:fatness` — moves each corner this far along its turned normal, default 0.0
  * `:scale` — the entity's scale the transforms carry, default 1.0
  * `:eye`, `:view_right` — where the view is and its right, for chrome textures
  * `:shell` — paints every mesh with another texture instead, unlit:
    `%{texture:, colour: {r, g, b, a}, axes: {forward, left, up}, time:, bulge:,
    scroll:}`; each corner is moved along its turned normal by `sin(s * width + time
    * speed) * height` (`bulge` `{width, height, speed}`, `s` its texture place over
    the texture's width), takes its place on the shell texture from the eye's
    reflection in it (`s = 1/2 + r_left / 2`, `t = 1/2 - r_up / 2`, `r` the
    reflection of the direction to `:eye` in the normal, along the entity's `axes`)
    moved by `scroll` `{ds, dt}` times `time`, and is tinted by the colour
  * `:blend`, `:depth`, `:cull` — as `Cauldron3D.Polygon`'s, for every triangle

# `sequence`

```elixir
@spec sequence(t(), String.t()) :: non_neg_integer() | nil
```

The index of the sequence named `name` (without regard to case), or `nil`.

# `vertices`

```elixir
@spec vertices(t(), {non_neg_integer(), non_neg_integer()}, tuple()) :: tuple()
```

The vertices of model `model` of body part `part`, each moved by its bone's transform.

---

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