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
@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
@spec add_group(t(), pos_integer(), binary()) :: t()
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).
@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.
@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 bit0x8000), and a turning controller's value (types0x08to0x20) is taken as degrees:controllers— up to four settings 0 to 255 instead, each standing forstart + n / 255 * (end - start), and:mouththe 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 forn / 255 * 2 - 1:base—{sequence, frame, last}: bones before bonelasttake sequencesequenceatframeinstead
@spec missing_groups(t()) :: [pos_integer()]
The groups above 0 whose animation add_group/3 has not yet supplied.
The model in data.
@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,directionin 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 bysin(s * width + time * speed) * height(bulge{width, height, speed},sits 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,rthe reflection of the direction to:eyein the normal, along the entity'saxes) moved byscroll{ds, dt}timestime, and is tinted by the colour:blend,:depth,:cull— asCauldron3D.Polygon's, for every triangle
@spec sequence(t(), String.t()) :: non_neg_integer() | nil
The index of the sequence named name (without regard to case), or nil.
@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.