diff --git a/crates/engine/src/model.rs b/crates/engine/src/model.rs index c4d7e17..faa2fef 100644 --- a/crates/engine/src/model.rs +++ b/crates/engine/src/model.rs @@ -26,6 +26,17 @@ pub enum ModelError { Geometry(String), } +/// Привязка вершины к костям (до трёх, как `mstudiovertex_t`). +/// +/// Индексы костей — **глобальные** (после fixup VTX), по ним считается +/// матрица скиннинга вместе со скелетом из `Model::bones` (A26). +#[derive(Debug, Clone, Copy, Default, PartialEq)] +pub struct VertexSkin { + pub bones: [u8; 3], + pub weights: [f32; 3], + pub num_bones: u8, +} + /// Вершина модели. #[derive(Debug, Clone, Copy, Default, PartialEq, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] @@ -58,6 +69,8 @@ pub struct Model { pub sequences: Vec, /// Кости скелета. pub bones: Vec, + /// Привязка вершин к костям, по одной на `vertices`. + pub skins: Vec, } /// Загружает модель по пути `models/...mdl`. @@ -98,12 +111,26 @@ pub fn load_model(vfs: &VirtualFs, name: &str, budget: &mut usize) -> Result 0 && s.weights.iter().any(|w| *w > 0.0)) + .count(); + tracing::info!( + name = %name, + vertices = model.skins.len(), + skinned, + "модель: привязка вершин к костям" + ); + } if !model.sequences.is_empty() { let sample: Vec<&str> = model .sequences @@ -205,6 +232,11 @@ pub fn load_model(vfs: &VirtualFs, name: &str, budget: &mut usize) -> Result Result` — привязку вершин +к костям из VVD (`bones[3]`, `weights[3]`, `num_bones`), по одной на вершину. +Замер на живых моделях: `companion_doll` 1471/1471, стол 136/136, диван 556/556 — +привязка есть у всех вершин; у пропов при этом один «корень» (bones=1). + +**Порядок работ:** (а) секвенции сделаны; дальше `.ani` (кадры) и слияние +включённых моделей; (б) привязка вершин сделана; (в) CPU-скиннинг в движке и +заливка на GPU; (г) проигрывание одной секвенции с циклом; (д) выбор по состоянию и блендинг. **Приёмка:** модель игрока/оружия двигается в кадре, ходьба переходит в бег и