Skip to main content
← PSoXide SDK guides and examples · SDK f2fec1c301f1 · All Cargo features enabled; check feature gates before use.

Model

Struct Model 

Source
pub struct Model {
    pub n_verts: usize,
    pub n_tris: usize,
    /* private fields */
}
Expand description

A parsed HMD8 blob: mesh, bone ranges, clip table and every clip’s composed bone palettes, all borrowed from one &'static slice with no copying.

Construct with Model::from_bytes or Model::from_bytes_with_vertex_cap; a blob that fails validation yields an empty model rather than panicking, so a bad cook draws nothing instead of taking the guest down.

Every accessor is memory-safe on any model, including one whose public counts (the deprecated n_verts and n_tris) were overwritten: reads are bounded by the blob and the private offsets from_bytes validated, never by the public counts. An index past a table reads an inert value (origin vertex, skipped triangle, identity pose). The *_unchecked forms skip that check for hot loops that already bound their index by the loaded count.

Fields§

§n_verts: usize
👎Deprecated:

use vertex_count()

§n_tris: usize
👎Deprecated:

use triangle_count()

Implementations§

Source§

impl Model

Source

pub const EMPTY: Model

Zero model for static cache slots (never drawn: no triangles).

Source

pub fn from_bytes(data: &'static [u8]) -> Model

Parse a HMD8 blob, guarding vertex count with DEFAULT_MAX_VERTICES.

Source

pub const fn vertex_count(&self) -> usize

Number of vertices in the stream.

Source

pub const fn triangle_count(&self) -> usize

Number of triangles in the face table.

Source

pub const fn frame_count(&self) -> usize

Number of baked palette frames across every clip.

Source

pub const fn clip_count(&self) -> usize

Number of clips in the clip table.

Source

pub const fn bone_count(&self) -> usize

Number of bones in each pose palette.

Source

pub const fn bone_range_count(&self) -> usize

Number of bone ranges (see Model::bone_range).

Source

pub const fn hitbox_count(&self) -> usize

Number of studio hitboxes (zero when the cook emitted none).

Source

pub fn from_bytes_with_vertex_cap( data: &'static [u8], max_verts: usize, ) -> Model

Parse a HMD8 blob, guarding vertex count with the caller’s own arena capacity.

Source

pub fn tracks(&self) -> Option<Tracks>

HMA1 local-space tracks, when the cook emitted them (validated at load). Most callers want Model::pose, which picks the right path.

Source

pub fn has_tracks(&self) -> bool

True when the model animates from HMA1 tracks. Its phase functions then return (clip, clip, pos_q8): pos_q8 is the source position in frames * 256, and Model::pose reads the triple the same way.

Source

pub fn pose<'a>( &'a self, frame: usize, frame2: usize, frac16: u32, mouth: u8, scratch: &'a mut [Affine], ) -> Pose<'a>

The bone transforms for one animation sample, whichever animation the model carries: a (frame, frame2, frac16) palette pair, or for HMA1 models (clip, _, pos_q8) decoded into scratch with the mouth controller opened mouth/64 on the jaw. A model with more HMA1 bones than scratch holds draws its bind pose.

Source

pub fn clip_end_pose(&self, clip: usize) -> (usize, usize, u32)

The sample holding clip’s final pose.

Source

pub fn local_to_world_q12(&self) -> u16

Source

pub fn clip_len(&self, clip: usize) -> usize

Source

pub fn clip_hold_ticks(&self, clip: usize) -> u16

GoldSrc source-sequence duration at the 20 Hz game clock. HMD8 cooks pack 100 ms quanta in ClipRec.frame_count’s unused high byte and duration bit 8 in ClipRec.first_frame’s unused high bit. A zero duration falls back safely instead of allowing a malformed clip to divide by zero.

Source

pub fn clip_frame(&self, clip: usize, local_frame: usize) -> usize

Source

pub fn looped_clip_phase( &self, clip: usize, duration: usize, elapsed: usize, ) -> (usize, usize, u32)

Source

pub fn one_shot_clip_phase( &self, clip: usize, duration: usize, elapsed: usize, ) -> (usize, usize, u32)

Source

pub fn frame(&self, frame: usize) -> ModelFrame<'_>

Source

pub fn bone_range(&self, index: usize) -> BoneRange

Bone range index, clamped to the last range. A model without ranges (the empty model) returns an empty range.

Source

pub fn hitbox(&self, index: usize) -> StudioHitbox

Studio hitbox index, clamped to the last one. A model without hitboxes returns a zero box on bone 0.

Source

pub fn vertex(&self, index: usize) -> Vec3I16

Bone-local position of vertex index. An index past the vertex stream reads as the origin instead of outside the blob; hot loops that have already bounded the index can use Self::vertex_unchecked.

Source

pub unsafe fn vertex_unchecked(&self, index: usize) -> Vec3I16

Self::vertex without the bounds check.

§Safety

index must be below the Model::vertex_count that Model::from_bytes gave this model. (The public count can be overwritten; the bound is the loaded value.)

Source

pub fn vertex_gte_words(&self, index: usize) -> GteVertexWords

Vertex index packed as the GTE’s VXY/VZ register words. An index past the vertex stream reads as the origin; see Self::vertex_gte_words_unchecked for bounded hot loops.

Source

pub fn vertex_words(&self) -> VertexWords<'static>

The whole vertex stream as a bounds-checked-once view; see VertexWords. Hot loops that walk a bone range read it through VertexWords::slice and VertexWords::triples instead of calling Self::vertex_gte_words per index.

Only the struct-of-arrays stream (HMD_FLAG_VERTEX_SOA, which every current cook sets) has this shape. An older interleaved cook, and the empty or a rejected model, return an empty view; read those through the per-index accessors.

Source

pub unsafe fn vertex_gte_words_unchecked(&self, index: usize) -> GteVertexWords

Self::vertex_gte_words without the bounds check.

§Safety

Same as Self::vertex_unchecked.

Source

pub fn frame_section_len(&self) -> usize

Byte length of the header + clips + ranges + static vertices + palettes – everything the per-frame draw needs. The TriRec/texture tail after it is only read by fill_render_faces_split_raw/triangle() (load-time or viewmodels), so the enemy pool can drop it once the faces are baked.

Source

pub fn compact_visible_bodies( blob: &mut [u8], visible_bodies: u8, remap: &mut [u16], ) -> Option<usize>

Compact the HMD8 blob in blob, in place, to the bodies in visible_bodies: whole bone/body ranges and their static vertices are dropped, and the shared pose palette, mouth transforms, hitboxes and HMA1 tracks move down unchanged. The triangle tail is not kept, so bake faces before compacting. Parse the result again with Model::from_bytes over blob[..len].

remap receives, for every old vertex index, its new index or u16::MAX when it was dropped; it needs one entry per vertex.

Returns the compact frame-section length, or None when blob is not a valid HMD8 model with body masks, remap is too short, or no visible body remains. blob may be partly rewritten on None after the range pass has started.

The buffer is borrowed exclusively, so no Model can read it while it changes: a model parsed from these bytes earlier must not be used again.

Source

pub fn triangle(&self, t: usize) -> Triangle

Triangle t, unpacked. An index past the triangle table reads as a degenerate triangle with an empty body mask, which every renderer skips; hot loops that have already bounded t can use Self::triangle_unchecked.

Source

pub unsafe fn triangle_unchecked(&self, t: usize) -> Triangle

Self::triangle without the bounds check.

§Safety

t must be below the Model::triangle_count that Model::from_bytes gave this model. (The public count can be overwritten; the bound is the loaded value.)

Source

pub fn triangle_uv_words(&self, t: usize) -> [u16; 3]

Packet-order UV words for a triangle whose other steady-render fields are already in the viewmodel pose cache. An index past the triangle table reads as zero; see Self::triangle_uv_words_unchecked.

Source

pub unsafe fn triangle_uv_words_unchecked(&self, t: usize) -> [u16; 3]

Self::triangle_uv_words without the bounds check.

§Safety

Same as Self::triangle_unchecked.

Source

pub fn has_body_masks(&self) -> bool

Source

pub fn triangle_normal(&self, t: usize) -> [i8; 3]

Triangle t’s face normal. An index past the triangle table reads as zero; see Self::triangle_normal_unchecked.

Source

pub unsafe fn triangle_normal_unchecked(&self, t: usize) -> [i8; 3]

Self::triangle_normal without the bounds check.

§Safety

Same as Self::triangle_unchecked.

Source

pub fn render_face_count(&self, visible_bodies: u8) -> usize

Number of consecutive texture/body-mask runs in authored face order.

Source

pub fn render_texture_mask(&self, visible_bodies: u8) -> u32

Source

pub fn render_face_run_count(&self, visible_bodies: u8) -> usize

Source

pub unsafe fn fill_render_faces_split_raw( &self, indices: *mut u32, payloads: *mut RenderFacePayload, runs: *mut u32, out_len: usize, visible_bodies: u8, ) -> (usize, usize)

Split GPU face records into a hot packed-index stream, a cold UV stream, and ordered texture runs. Each run word is end_face:u16 | tex:u8<<16 | body_mask:u8<<24; end_face is relative to this model. The caller guarantees enough face and run storage.

§Safety

indices and payloads must each be writable for out_len records. runs must be writable for the maximum texture/body run count reported by Self::render_face_run_count, and the outputs must not overlap.

Source

pub fn vert(&self, index: usize) -> Vec3I16

👎Deprecated:

renamed to vertex

Renamed to Model::vertex.

Source

pub fn tri_uv_words(&self, t: usize) -> [u16; 3]

👎Deprecated:

renamed to triangle_uv_words

Trait Implementations§

Source§

impl Clone for Model

Source§

fn clone(&self) -> Model

Returns a duplicate of the value. Read more
1.0.0§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for Model

Auto Trait Implementations§

§

impl Freeze for Model

§

impl RefUnwindSafe for Model

§

impl Send for Model

§

impl Sync for Model

§

impl Unpin for Model

§

impl UnsafeUnpin for Model

§

impl UnwindSafe for Model

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> CloneToUninit for T
where T: Clone,

§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.