Files
CFDManager/rust/crates/simv-editor/src/ui.rs
T
NotBigGhostandClaude Opus 5 f5c9e61589 Порт редактора SimVulcan на Rust
Та же программа на ash + winit + egui: орбитальная камера, три плоскости сетки,
загрузка .obj, три режима отображения, единый проход динамического рендеринга с
глубинным вложением и submit через synchronization2. Шейдеры общие с C++-версией —
build.rs компилирует их из shaders/ и вшивает SPIR-V в бинарь.

Пять крейтов повторяют карту целей CMake, но инвариант «весь Vulkan в одном месте»
теперь проверяет компилятор: у simv-mesh и simv-editor нет ash в зависимостях.

Расхождения, вынужденные языком, а не вкусом:
 * App переехал в исполняемый крейт — в C++ граф целей цикличен (core владеет vk,
   vk принимает core::Window&), и держится это лишь на том, что simv_vk видит
   заголовки core, не линкуя цель; Cargo такой цикл отвергает;
 * состояние сцены возвращается из замыкания интерфейса, а не ставится сеттерами:
   замыкание вызывается из метода рендерера, трогать рендерер оттуда нельзя;
 * выгрузка меша на видеокарту уехала из середины записи кадра, где C++-версия
   зовёт vkDeviceWaitIdle уже после захвата образа цепочки показа.

Vulkan-контекст и цепочка показа написаны руками: замены vk-bootstrap в экосистеме
Rust нет. Обёртки Buffer и Image, лежащие в C++-версии без применения, здесь
задействованы всеми потребителями.

13 тестов, из них три перенесены из Catch2 дословно. cargo clippy -D warnings чист,
слой валидации молчит.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 20:58:19 +03:00

81 lines
3.3 KiB
Rust

//! Панель вьюпорта — перенос `simv::editor::EditorUI`.
//!
//! Про [`DisplayMode`]. Панель обязана как-то сообщить рендереру выбранный режим, но
//! `simv_vk::RenderMode` ей недоступен: `simv-editor` не зависит от `simv-vk`, и это
//! проверяет компилятор. C++ обходит ту же границу, возвращая из `EditorUI` голый
//! `int`, который `main.cpp` приводит через `static_cast` к `vk::RenderMode` —
//! связь между двумя перечислениями там держится на честном слове. Здесь у панели
//! свой типизированный режим, а разбор в `simv_vk::RenderMode` делает `simv-app`
//! обычным `match`: добавите вариант — компилятор укажет, где его дописать.
use crate::camera::{Camera, ViewPlane};
/// Как отображать модель. Соответствует `simv_vk::RenderMode`, но живёт по эту
/// сторону границы модулей.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum DisplayMode {
#[default]
Solid,
Wireframe,
SolidWire,
}
pub struct EditorUi {
display_mode: DisplayMode,
grid_visible: bool,
}
impl Default for EditorUi {
fn default() -> Self {
Self {
display_mode: DisplayMode::Solid,
grid_visible: true,
}
}
}
impl EditorUi {
pub fn display_mode(&self) -> DisplayMode {
self.display_mode
}
pub fn grid_visible(&self) -> bool {
self.grid_visible
}
pub fn draw(&mut self, ctx: &egui::Context, camera: &mut Camera) {
egui::Window::new("Viewport")
.default_size([280.0, 200.0])
.show(ctx, |ui| {
ui.label(egui::RichText::new("Камера").weak());
ui.horizontal(|ui| {
if ui.button("Спереди").clicked() {
camera.align_to_plane(ViewPlane::Front);
}
if ui.button("Сверху").clicked() {
camera.align_to_plane(ViewPlane::Top);
}
if ui.button("Сбоку").clicked() {
camera.align_to_plane(ViewPlane::Side);
}
});
ui.label(
egui::RichText::new("Перетаскивание — поворот, колесо — приближение").weak(),
);
ui.separator();
ui.label(egui::RichText::new("Отображение модели").weak());
ui.radio_value(&mut self.display_mode, DisplayMode::Solid, "Заливка");
ui.radio_value(&mut self.display_mode, DisplayMode::Wireframe, "Каркас");
ui.radio_value(
&mut self.display_mode,
DisplayMode::SolidWire,
"Заливка с каркасом",
);
ui.separator();
ui.checkbox(&mut self.grid_visible, "Показывать сетку");
});
}
}