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.
Summary
Functions
The animation names, sorted.
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.
The name of every pose in the .mdl file bytes, in pose order (a frame group's poses each have one).
Every handle the model holds.
An instance of the model's first pose with materials at transform.
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).
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.
Whether the game spins the model about its up axis (model flag 8, pickups).
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.
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.
Types
@type sample() :: {non_neg_integer(), non_neg_integer(), float()}
@type skin() :: %{base: CauldronGPU.Handle.t(), glow: CauldronGPU.Handle.t() | nil}
@type t() :: %CauldronGPU.AliasModel{ animations: %{required(String.t()) => [non_neg_integer()]}, flags: non_neg_integer(), frames: tuple(), names: tuple(), poses: tuple(), skins: [skin()] }
Functions
@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.
The animation names, sorted.
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.
The name of every pose in the .mdl file bytes, in pose order (a frame group's poses each have one).
@spec handles(t()) :: [CauldronGPU.Handle.t()]
Every handle the model holds.
@spec instance(t(), [CauldronGPU.Handle.t()], CauldronGPU.Transform.t()) :: CauldronGPU.Instance.t()
An instance of the model's first pose with materials at transform.
@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).
@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.
Whether the game spins the model about its up axis (model flag 8, pickups).
@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.
@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.