1 unstable release
new 0.3.0 | Nov 20, 2024 |
---|
#10 in #graphics-engine
515KB
2K
SLoC
WGPU Graphics
A minimal graphics engine for rust programs. It's a framework for building PC applications that have 3D graphics, and an EGUI user interface. It uses the WGPU toolkit, and Vulkan backend. It works on Windows, Linux, and Mac, but does not support web.
This is intended as a personal tool for use in various projects, including wave-function viewing, n-body simulations, and a protein viewer. I'm publishing this in case others find use in it directly, or in the source code as an example.
It currently does not include practical documentation or usage examples.
It includes built in FPS-style (Amplified for 6 DOF) camera controls. (WSAD + Space for up, C for down, Q and E for roll.
Mouse to look). This can be overridden by the application with arbitrary controls. (See the event_handler
parameter to
graphics::run()
)
Example boilerplate below. Calling render(state)
starts an event loop. This currently performs no actions from (mouse, keyboard etc)
events and each frame; actions are driven from the UI, and from the engine's built-in control scheme for moving the camera.
//! This module integrations this application with the graphics engine.
use std::f32::consts::TAU;
use graphics::{
Camera, ControlScheme, DeviceEvent, EngineUpdates, Entity, InputSettings, LightType, Lighting,
Mesh, PointLight, Scene, UiLayout, UiSettings,
};
use egui::{Context, Slider, TopBottomPanel};
use lin_alg::f32::{Quaternion, Vec3};
use crate::{playback::change_snapshot, ui::ui_handler, State};
type Color = (f32, f32, f32);
const WINDOW_TITLE: &str = "Causal gravity model";
const WINDOW_SIZE_X: f32 = 1_600.;
const WINDOW_SIZE_Y: f32 = 1_000.;
const BACKGROUND_COLOR: Color = (0.5, 0.5, 0.5);
const RENDER_DIST: f32 = 200.;
pub const BODY_SPHERE_SIZE: f32 = 0.1;
pub const BODY_SHINYNESS: f32 = 2.;
pub const BODY_COLOR: Color = (0., 1.0, 0.5);
fn event_handler(
_state: &mut State,
_event: DeviceEvent,
_scene: &mut Scene,
_dt: f32,
) -> EngineUpdates {
EngineUpdates::default()
}
/// This runs each frame. Currently, no updates.
fn render_handler(_state: &mut State, _scene: &mut Scene, _dt: f32) -> EngineUpdates {
EngineUpdates::default()
}
const SLIDER_WIDTH: f32 = 460.;
const SLIDER_WIDTH_ORIENTATION: f32 = 100.;
pub const ROW_SPACING: f32 = 22.;
pub const COL_SPACING: f32 = 30.;
/// This function draws the (immediate-mode) GUI.
/// [UI items](https://docs.rs/egui/latest/egui/struct.Ui.html#method.heading)
pub fn ui_handler(state: &mut State, ctx: &Context, scene: &mut Scene) -> EngineUpdates {
let mut engine_updates = EngineUpdates::default();
TopBottomPanel::top("0").show(ctx, |ui| {
ui.spacing_mut().slider_width = SLIDER_WIDTH;
ui.horizontal(|ui| {
ui.add_space(COL_SPACING);
ui.label("Time:");
let snapshot_prev = state.ui.snapshot_selected;
ui.add(Slider::new(
&mut state.ui.snapshot_selected,
0..=state.snapshots.len() - 1,
));
if state.ui.snapshot_selected != snapshot_prev {
change_snapshot(
&mut scene.entities,
&state.snapshots[state.ui.snapshot_selected],
);
engine_updates.entities = true;
}
});
ui.add_space(ROW_SPACING / 2.);
});
engine_updates
}
/// Entry point to our render and event loop.
pub fn render(state: State) {
let mut scene = Scene {
meshes: vec![Mesh::new_sphere(1., 12, 12)],
entities: Vec::new(), // updated below.
camera: Camera {
fov_y: TAU / 8.,
position: Vec3::new(0., 10., -20.),
far: RENDER_DIST,
orientation: Quaternion::from_axis_angle(Vec3::new(1., 0., 0.), TAU / 16.),
..Default::default()
},
lighting: Lighting {
ambient_color: [-1., 1., 1., 0.5],
ambient_intensity: 0.03,
point_lights: vec![
// Light from above and to a side.
PointLight {
type_: LightType::Omnidirectional,
position: Vec3::new(30., 50., 30.),
diffuse_color: [0.3, 0.4, 0.5, 1.],
specular_color: [0.3, 0.4, 0.5, 1.],
diffuse_intensity: 8_000.,
specular_intensity: 30_000.,
},
],
},
background_color: BACKGROUND_COLOR,
window_size: (WINDOW_SIZE_X, WINDOW_SIZE_Y),
window_title: WINDOW_TITLE.to_owned(),
};
let input_settings = InputSettings {
initial_controls: ControlScheme::FreeCamera,
..Default::default()
};
let ui_settings = UiSettings {
layout: UiLayout::Top,
icon_path: Some("./resources/icon.png".to_owned()),
};
// Initialize entities.
if !state.snapshots.is_empty() {
change_snapshot(
&mut scene.entities,
&state.snapshots[state.ui.snapshot_selected],
)
}
graphics::run(
state,
scene,
input_settings,
ui_settings,
render_handler,
event_handler,
ui_handler,
);
}
Dependencies
~20–55MB
~1M SLoC