From 7490ef379892802f76d8d4e5e2274f077c0fe2ee Mon Sep 17 00:00:00 2001 From: Niko Marmeladkov Date: Fri, 23 Oct 2026 13:28:49 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82=D1=8B=20=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=BE=D1=80=D1=83=D0=B6=D0=B8?= =?UTF-8?q?=D1=8F:=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D0=B0,=20VVD,=20=D1=81=D0=BA=D0=B8=D0=BD=D0=BD=D0=B8=D0=BD?= =?UTF-8?q?=D0=B3=20=D0=B2=D1=8C=D1=8E=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Замеры на реальных ассетах, которые пригодятся для дальнейших правок: - v_pistol: 1 бодипарт, 2 меша (1169/4356 и 988/2448), 2157 вершин, 2167 в VVD (10 не используются), кадры лежат внутри MDL, флаги дорожек 2/3/8/12 (RAWROT/RAWPOS/ANIMROT/ANIMPOS); - скиннинг вьюмодели не разваливает меш: между позой покоя и кадром idle01 максимальный сдвиг 4 юнита, габарит 20×23×13; - v_pistol рисуется «лепестком» даже в позе покоя (до скиннинга) — проблема загрузки геометрии именно этой модели, отложена; по умолчанию w_pistol. Все сьюты зелёные. --- crates/engine/tests/world_real.rs | 74 +++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/crates/engine/tests/world_real.rs b/crates/engine/tests/world_real.rs index 671b7ea..d3f48b5 100644 --- a/crates/engine/tests/world_real.rs +++ b/crates/engine/tests/world_real.rs @@ -537,6 +537,80 @@ fn real_weapon_model_structure() { } } +/// Заголовки VVD моделей оружия: версия, смещения, размеры. +#[test] +fn real_weapon_vvd_headers() { + let Some(root) = assets_root() else { + eprintln!("пропуск: ассеты не найдены"); + return; + }; + let vfs = mount_game(&root); + for name in [ + "models/weapons/v_pistol", + "models/weapons/w_pistol", + "models/props_c17/oildrum001", + ] { + let Ok(data) = vfs.read(&format!("{name}.vvd")) else { + continue; + }; + let vvd = formats::vvd::Vvd::parse(&data).expect("разбор vvd"); + let h = &vvd.header; + eprintln!( + "{name}.vvd: байт {}, версия {}, лодов {}, вершин по LOD {:?}, фиксов {}, \ + vertex_data_start {}, tangent_data_start {}", + data.len(), + h.version, + h.num_lods, + &h.num_lod_vertexes[..h.num_lods.clamp(0, 8) as usize], + h.num_fixups, + h.vertex_data_start, + h.tangent_data_start + ); + let verts = vvd.vertices(0).expect("вершины"); + let mut min = [f32::MAX; 3]; + let mut max = [f32::MIN; 3]; + for v in &verts { + for j in 0..3 { + min[j] = min[j].min(v.position[j]); + max[j] = max[j].max(v.position[j]); + } + } + eprintln!(" габарит вершин: min {min:?}, max {max:?}"); + } +} + +/// Габарит модели из заголовка MDL: должен совпадать с вершинами VVD. +#[test] +fn real_weapon_mdl_hull() { + let Some(root) = assets_root() else { + eprintln!("пропуск: ассеты не найдены"); + return; + }; + let vfs = mount_game(&root); + for name in ["models/weapons/v_pistol", "models/weapons/w_pistol"] { + let Ok(data) = vfs.read(&format!("{name}.mdl")) else { + continue; + }; + // studiohdr_t: hull_min — 104, hull_max — 116. + let f = |o: usize| f32::from_le_bytes([data[o], data[o + 1], data[o + 2], data[o + 3]]); + eprintln!( + "{name}: hull_min [{:.1} {:.1} {:.1}], hull_max [{:.1} {:.1} {:.1}], view_bb [{:.1} {:.1} {:.1}]..[{:.1} {:.1} {:.1}]", + f(104), + f(108), + f(112), + f(116), + f(120), + f(124), + f(128), + f(132), + f(136), + f(140), + f(144), + f(148) + ); + } +} + /// Позиции вершин экземпляра модели. fn instance_positions(world: &engine::World, index: usize) -> Vec<[f32; 3]> { let instance = &world.model_instances[index];