CauldronGPU.AliasModel (CauldronGPU v0.3.3)

Copy Markdown View Source

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

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

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

sample()

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

skin()

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

t()

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

Functions

animate(model, id, instance, animation, time, opts \\ [])

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

animations(alias_model)

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

The animation names, sorted.

frame(alias_model, frame, time)

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

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

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

Every handle the model holds.

instance(alias_model, materials, transform)

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

material(gpu, alias_model, opts \\ [])

@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(alias_model, id, instance, arg)

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?(alias_model)

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

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

sample(alias_model, animation, time, opts \\ [])

@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(gpu, mdl, palette, opts \\ [])

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