Cauldron3D.Formats.Studio (Cauldron3D.Formats v0.2.2)

Copy Markdown View Source

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.

Summary

Functions

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

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

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

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.

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

The model in data.

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

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

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

Types

t()

@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()
}

Functions

add_group(studio, group, data)

@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(studio, data)

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

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

body_model(studio, part, body)

@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(studio, index, frame, opts \\ [])

@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(studio)

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

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

parse(data)

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

The model in data.

polygons(studio, transforms, opts \\ [])

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

The model's triangles as Cauldron3D.Polygons, 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(studio, name)

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

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

vertices(studio, arg, transforms)

@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.