Model теперь несёт skins: Vec<VertexSkin> — привязку вершин к костям из VVD (до трёх костей с весами и их число), по одной записи на вершину. Индексы глобальные (после fixup VTX), считаться по ним будет матрица скиннинга вместе со скелетом Model::bones. Замер на живых моделях (лог «модель загружена»): companion_doll 1471 вершин / 1471 привязок, стол 136/136, диван 556/556 — привязка есть у всех вершин; у пропов при этом один корневой узел (bones=1). Модель игрока police.mdl — 57 костей. Все 23 сьюта зелёные; docs/PARITY.md A26 обновлён.
This commit is contained in:
parent
d7796974b1
commit
f8db4cf73c
2 changed files with 48 additions and 3 deletions
|
|
@ -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<formats::mdl::Sequence>,
|
||||
/// Кости скелета.
|
||||
pub bones: Vec<formats::mdl::Bone>,
|
||||
/// Привязка вершин к костям, по одной на `vertices`.
|
||||
pub skins: Vec<VertexSkin>,
|
||||
}
|
||||
|
||||
/// Загружает модель по пути `models/...mdl`.
|
||||
|
|
@ -98,12 +111,26 @@ pub fn load_model(vfs: &VirtualFs, name: &str, budget: &mut usize) -> Result<Mod
|
|||
name: name.clone(),
|
||||
sequences: Vec::new(),
|
||||
bones: Vec::new(),
|
||||
skins: Vec::new(),
|
||||
};
|
||||
|
||||
// Скелет и секвенции: их читает `formats`, движку они нужны для
|
||||
// скиннинга (A26). Отсутствие данных не ошибка: часть моделей их не имеет.
|
||||
model.bones = mdl.bones().unwrap_or_default();
|
||||
model.sequences = mdl.sequences().unwrap_or_default();
|
||||
if !model.skins.is_empty() {
|
||||
let skinned = model
|
||||
.skins
|
||||
.iter()
|
||||
.filter(|s| s.num_bones > 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<Mod
|
|||
normal: source.normal,
|
||||
uv: source.texcoord,
|
||||
});
|
||||
model.skins.push(VertexSkin {
|
||||
bones: source.bones,
|
||||
weights: source.weights,
|
||||
num_bones: source.num_bones,
|
||||
});
|
||||
let pos = Vec3::new(source.position[0], source.position[1], source.position[2]);
|
||||
model.bounds_min = model.bounds_min.min(pos);
|
||||
model.bounds_max = model.bounds_max.max(pos);
|
||||
|
|
@ -238,6 +270,14 @@ pub fn load_model(vfs: &VirtualFs, name: &str, budget: &mut usize) -> Result<Mod
|
|||
model.bounds_min = Vec3::splat(-16.0);
|
||||
model.bounds_max = Vec3::splat(16.0);
|
||||
}
|
||||
tracing::info!(
|
||||
name = %name,
|
||||
vertices = model.vertices.len(),
|
||||
skins = model.skins.len(),
|
||||
bones = model.bones.len(),
|
||||
sequences = model.sequences.len(),
|
||||
"модель загружена"
|
||||
);
|
||||
Ok(model)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3177,9 +3177,14 @@ Sandbox.
|
|||
моделях** (`police.mdl` включает общую модель игрока), и их надо сливать по
|
||||
`mdl.include_models()`.
|
||||
|
||||
**Порядок работ:** (а) секвенции и `.ani` — секвенции сделаны, `.ani` и
|
||||
включённые модели дальше; (б) `ModelVertex` с костями/весами; (в) CPU-скиннинг в
|
||||
движке и заливка на GPU; (г) проигрывание одной секвенции с циклом; (д) выбор по
|
||||
**Сделано (шаг б).** `Model` несёт `skins: Vec<VertexSkin>` — привязку вершин
|
||||
к костям из VVD (`bones[3]`, `weights[3]`, `num_bones`), по одной на вершину.
|
||||
Замер на живых моделях: `companion_doll` 1471/1471, стол 136/136, диван 556/556 —
|
||||
привязка есть у всех вершин; у пропов при этом один «корень» (bones=1).
|
||||
|
||||
**Порядок работ:** (а) секвенции сделаны; дальше `.ani` (кадры) и слияние
|
||||
включённых моделей; (б) привязка вершин сделана; (в) CPU-скиннинг в движке и
|
||||
заливка на GPU; (г) проигрывание одной секвенции с циклом; (д) выбор по
|
||||
состоянию и блендинг.
|
||||
|
||||
**Приёмка:** модель игрока/оружия двигается в кадре, ходьба переходит в бег и
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue