worlds-history-sim-rs/src/main.rs

306 lines
10 KiB
Rust
Raw Normal View History

use gui::widgets::{InfoPanel, ToolbarWidget};
2022-08-25 15:45:45 +02:00
pub(crate) mod components;
#[cfg(feature = "render")]
pub(crate) mod gui;
pub(crate) mod macros;
pub(crate) mod plugins;
pub(crate) mod resources;
2022-09-05 11:43:50 +02:00
use {
bevy::{
app::App,
log::LogSettings,
prelude::{IntoExclusiveSystem, World},
utils::{default, tracing::Level},
},
bevy_egui::egui::{FontData, FontDefinitions, FontFamily},
planet::WorldManager,
plugins::WorldPlugins,
2022-09-06 20:40:27 +02:00
};
#[cfg(feature = "render")]
2022-09-19 12:48:13 +02:00
use {
bevy::{
asset::Assets,
core_pipeline::core_2d::{Camera2d, Camera2dBundle},
ecs::{
change_detection::{Mut, ResMut},
query::With,
2022-09-19 12:48:13 +02:00
system::{Commands, Query, Res},
},
prelude::Vec2,
render::{
camera::{Camera, RenderTarget},
render_resource::{
Extent3d,
TextureDescriptor,
TextureDimension,
TextureFormat,
TextureUsages,
},
texture::{Image, ImageSettings},
},
sprite::{Sprite, SpriteBundle},
transform::components::GlobalTransform,
window::{WindowDescriptor, Windows},
2022-09-19 12:48:13 +02:00
winit::WinitSettings,
},
bevy_egui::EguiContext,
components::panning::Pan2d,
gui::widget,
2022-09-19 12:48:13 +02:00
resources::CursorMapPosition,
2022-09-19 10:54:10 +02:00
};
#[cfg(all(feature = "render", feature = "globe_view"))]
use {
bevy::{
asset::Handle,
core_pipeline::core_3d::Camera3dBundle,
pbr::{PbrBundle, PointLight, PointLightBundle, StandardMaterial},
prelude::{Quat, Vec3},
render::camera::OrthographicProjection,
render::mesh::{shape::UVSphere, Mesh},
transform::components::Transform,
},
std::f32::consts::FRAC_PI_2,
};
2022-08-25 15:45:45 +02:00
#[cfg(feature = "render")]
fn update_cursor_map_position(
mut cursor_map_position: ResMut<'_, CursorMapPosition>,
transform: Query<'_, '_, (&Camera, &GlobalTransform), With<Camera2d>>,
windows: Res<'_, Windows>,
world_manager: Res<'_, WorldManager>,
) {
let (camera, transform) = transform.single();
let window = match camera.target {
RenderTarget::Window(window_id) => windows.get(window_id).unwrap(),
RenderTarget::Image(_) => windows.primary(),
};
if let Some(screen_position) = window.cursor_position() {
let window_size = Vec2::new(window.width(), window.height());
// GPU coordinates [-1..1]
let ndc = (screen_position / window_size) * 2.0 - Vec2::ONE;
// Matrix to reverse camera transform
let ndc_to_world = transform.compute_matrix() * camera.projection_matrix().inverse();
let world_position =
ndc_to_world.project_point3(ndc.extend(-1.0)).truncate() / WORLD_SCALE as f32;
let world = world_manager.world();
cursor_map_position.x = world.width as i32 / 2 + f32::ceil(world_position.x) as i32 - 1;
cursor_map_position.y = world.height as i32 / 2 + f32::ceil(world_position.y) as i32 - 1;
}
}
#[cfg(all(feature = "render", feature = "globe_view"))]
const ROTATION_SPEED: f32 = 0.002;
#[cfg(all(feature = "render", feature = "globe_view"))]
fn rotate_globe(mut globe_transform: Query<'_, '_, &mut Transform, With<Handle<Mesh>>>) {
globe_transform.single_mut().rotate_y(ROTATION_SPEED);
}
#[cfg(feature = "render")]
fn generate_graphics(
mut commands: Commands<'_, '_>,
2022-09-19 12:48:13 +02:00
mut world_manager: ResMut<'_, WorldManager>,
mut images: ResMut<'_, Assets<Image>>,
mut egui_context: ResMut<'_, EguiContext>,
#[cfg(feature = "globe_view")] mut materials: ResMut<'_, Assets<StandardMaterial>>,
#[cfg(feature = "globe_view")] mut meshes: ResMut<'_, Assets<Mesh>>,
) {
// Add Julia-Mono font to egui
{
let ctx = egui_context.ctx_mut();
let mut fonts = FontDefinitions::default();
const FONT_NAME: &str = "Julia-Mono";
_ = fonts.font_data.insert(
FONT_NAME.to_owned(),
FontData::from_static(include_bytes!("../assets/JuliaMono.ttf")),
);
fonts
.families
.get_mut(&FontFamily::Monospace)
.expect("Failed to get 'Monospace' FontFamily")
.insert(0, FONT_NAME.to_owned());
fonts
.families
.get_mut(&FontFamily::Proportional)
.expect("Failed to get 'Proportional' FontFamily")
.push(FONT_NAME.to_owned());
ctx.set_fonts(fonts);
}
let world = world_manager.world();
let custom_sprite_size = Vec2 {
x: (WORLD_SCALE * world.width as i32) as f32,
y: (WORLD_SCALE * world.height as i32) as f32,
};
// Set up 2D map mode
{
let map_image_handle = images.add(Image {
data: world_manager.map_color_bytes(),
texture_descriptor: TextureDescriptor {
label: None,
size: Extent3d {
width: world.width,
height: world.height,
..default()
},
dimension: TextureDimension::D2,
format: TextureFormat::Rgba32Float,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
},
..default()
});
world_manager.map_image_handle_id = Some(map_image_handle.id);
_ = commands
.spawn_bundle(Camera2dBundle::default())
.insert(Pan2d::new());
// TODO: Switch to egui
_ = commands.spawn_bundle(SpriteBundle {
texture: images.get_handle(world_manager.map_image_handle_id.unwrap()),
sprite: Sprite {
custom_size: Some(custom_sprite_size),
..default()
},
..default()
});
}
#[cfg(feature = "globe_view")]
{
let world = world_manager.world();
let globe_image_handle = images.add(Image {
data: world_manager.globe_color_bytes(),
texture_descriptor: TextureDescriptor {
label: None,
size: Extent3d {
width: world.width,
height: world.height,
..default()
},
dimension: TextureDimension::D2,
format: TextureFormat::Rgba32Float,
mip_level_count: 1,
sample_count: 1,
usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
},
..default()
});
world_manager.globe_image_handle_id = Some(globe_image_handle.id);
_ = commands.spawn_bundle(Camera3dBundle {
camera: Camera {
is_active: false,
..default()
},
transform: Transform::from_xyz(0.0, 0.0, 8.0).looking_at(default(), Vec3::Y),
projection: OrthographicProjection {
scale: 0.01,
..default()
}
.into(),
..default()
});
let globe_material_handle = materials.add(
images
.get_handle(world_manager.globe_image_handle_id.unwrap())
.into(),
);
world_manager.globe_material_handle_id = Some(globe_material_handle.id);
// TODO: Globe texture is mirrored east-to-west.
_ = commands.spawn_bundle(PbrBundle {
mesh: meshes.add(Mesh::from(UVSphere {
radius: 2.0,
..default()
})),
material: globe_material_handle,
transform: Transform::from_rotation(Quat::from_rotation_x(FRAC_PI_2)),
..default()
});
_ = commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(-20.0, 0.0, 50.0),
point_light: PointLight {
intensity: 600000.,
range: 100.,
..default()
},
..default()
});
}
}
fn update_gui(world: &mut World) {
debug_assert!(world.contains_resource::<WorldManager>());
world.resource_scope(|world, mut ctx: Mut<'_, EguiContext>| {
let ctx = ctx.ctx_mut();
_ = bevy_egui::egui::Window::new("Info panel")
.resizable(false)
.show(ctx, |ui| {
widget::<InfoPanel<'_, '_>>(world, ui, "Map Info Panel".into());
});
2022-09-19 12:48:13 +02:00
_ = bevy_egui::egui::TopBottomPanel::bottom("Toolbar")
.resizable(false)
.default_height(30.0)
.show(ctx, |ui| {
widget::<ToolbarWidget<'_, '_>>(world, ui, "Toolbar".into());
});
});
}
2022-09-06 20:40:27 +02:00
#[cfg(feature = "render")]
const WORLD_SCALE: i32 = 4;
fn main() -> Result<(), Box<dyn std::error::Error>> {
2022-09-05 11:43:50 +02:00
let mut app = App::new();
let mut manager = WorldManager::new();
2022-09-05 11:43:50 +02:00
#[cfg(feature = "render")]
{
let world = manager.new_world()?;
_ = app
.insert_resource(WinitSettings::game())
2022-09-05 11:43:50 +02:00
// Use nearest-neighbor rendering for cripsier pixels
.insert_resource(ImageSettings::default_nearest())
.insert_resource(WindowDescriptor {
width: (WORLD_SCALE * world.width as i32) as f32,
height: (WORLD_SCALE * world.height as i32) as f32,
2022-09-05 11:43:50 +02:00
title: String::from("World-RS"),
resizable: true,
..default()
})
.insert_resource(CursorMapPosition::default())
.add_startup_system(generate_graphics)
.add_system(update_gui.exclusive_system())
.add_system(update_cursor_map_position);
#[cfg(all(feature = "render", feature = "globe_view"))]
{
_ = app.add_system(rotate_globe);
}
2022-09-05 11:43:50 +02:00
}
#[cfg(not(feature = "render"))]
{
_ = manager.new_world()?
}
_ = app.insert_resource(LogSettings {
#[cfg(feature = "logging")]
level: Level::DEBUG,
#[cfg(not(feature = "logging"))]
level: Level::WARN,
..default()
});
app.add_plugins(WorldPlugins).insert_resource(manager).run();
Ok(())
2022-08-25 15:45:45 +02:00
}