Та же программа на 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>
81 lines
3.3 KiB
Rust
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, "Показывать сетку");
|
|
});
|
|
}
|
|
}
|