Implement offscreen rendering
This commit is contained in:
parent
64d474694f
commit
13a912d228
3 changed files with 365 additions and 286 deletions
|
@ -1,6 +1,6 @@
|
||||||
use bevy::prelude::*;
|
use bevy::{prelude::*, render::view::RenderLayers};
|
||||||
|
|
||||||
#[derive(Resource, Debug, PartialEq, Clone)]
|
#[derive(Resource, Debug, Clone)]
|
||||||
pub struct GlobalState {
|
pub struct GlobalState {
|
||||||
pub cursor_position: Vec2,
|
pub cursor_position: Vec2,
|
||||||
pub window_extent: Vec2,
|
pub window_extent: Vec2,
|
||||||
|
@ -8,39 +8,22 @@ pub struct GlobalState {
|
||||||
pub view_2d: bool,
|
pub view_2d: bool,
|
||||||
pub extrusion_height: f32,
|
pub extrusion_height: f32,
|
||||||
|
|
||||||
pub offscreen_image
|
pub offscreen_image: Handle<Image>,
|
||||||
|
pub offscreen_image_size: Vec2,
|
||||||
|
pub offscreen_render_layer: RenderLayers,
|
||||||
|
|
||||||
camera_3d: Option<Entity>,
|
pub camera_3d: Entity,
|
||||||
camera_2d: Option<Entity>,
|
pub camera_2d: Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalState {
|
impl GlobalState {
|
||||||
pub fn setup_2d_cam(&mut self, cmd: &mut Commands) {
|
pub fn new(
|
||||||
self.camera_2d = Some(cmd.spawn(Camera2dBundle::default()).id());
|
camera_2d: Entity,
|
||||||
}
|
camera_3d: Entity,
|
||||||
|
offscreen_image: Handle<Image>,
|
||||||
pub fn setup_3d_cam(&mut self, cmd: &mut Commands) {
|
offscreen_image_size: Vec2,
|
||||||
self.camera_3d = Some(
|
offscreen_render_layer: RenderLayers,
|
||||||
cmd.spawn(Camera3dBundle {
|
) -> Self {
|
||||||
transform: Transform::from_xyz(200.0, -600.0, 600.0)
|
|
||||||
.looking_at((self.window_extent * 0.5).extend(0.0), Vec3::Z),
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.id(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn camera_2d(&self) -> Entity {
|
|
||||||
self.camera_2d.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn camera_3d(&self) -> Entity {
|
|
||||||
self.camera_3d.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GlobalState {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
cursor_position: Vec2::default(),
|
cursor_position: Vec2::default(),
|
||||||
window_extent: Vec2::default(),
|
window_extent: Vec2::default(),
|
||||||
|
@ -48,10 +31,12 @@ impl Default for GlobalState {
|
||||||
view_2d: true,
|
view_2d: true,
|
||||||
extrusion_height: 20.0,
|
extrusion_height: 20.0,
|
||||||
|
|
||||||
camera_3d: None,
|
offscreen_image,
|
||||||
camera_2d: None,
|
offscreen_image_size,
|
||||||
|
offscreen_render_layer,
|
||||||
|
|
||||||
|
camera_3d,
|
||||||
|
camera_2d,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive()]
|
|
||||||
|
|
145
src/main.rs
145
src/main.rs
|
@ -7,10 +7,17 @@ use bevy::{
|
||||||
ButtonState,
|
ButtonState,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
render::{
|
||||||
|
camera::RenderTarget,
|
||||||
|
render_resource::{
|
||||||
|
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
|
||||||
|
},
|
||||||
|
view::RenderLayers,
|
||||||
|
},
|
||||||
window::WindowResized,
|
window::WindowResized,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bevy_egui::{egui, EguiContexts, EguiPlugin};
|
use bevy_egui::{egui, EguiContexts, EguiPlugin, EguiUserTextures};
|
||||||
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
use bevy_inspector_egui::quick::WorldInspectorPlugin;
|
||||||
use global_state::GlobalState;
|
use global_state::GlobalState;
|
||||||
use polygon::ObjectInfos;
|
use polygon::ObjectInfos;
|
||||||
|
@ -20,17 +27,77 @@ fn main() {
|
||||||
.add_plugins(DefaultPlugins)
|
.add_plugins(DefaultPlugins)
|
||||||
.add_plugin(EguiPlugin)
|
.add_plugin(EguiPlugin)
|
||||||
.add_plugin(WorldInspectorPlugin::new())
|
.add_plugin(WorldInspectorPlugin::new())
|
||||||
.insert_resource(GlobalState::default())
|
|
||||||
.insert_resource(ObjectInfos::default())
|
.insert_resource(ObjectInfos::default())
|
||||||
.add_startup_system(setup_cameras)
|
.add_startup_system(setup)
|
||||||
.add_systems((mouse_event_system, update_ui_side_panel, on_resize_system))
|
.add_systems((mouse_event_system, update_ui_side_panel, on_resize_system))
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spawns the camera
|
// Creates GlobalState
|
||||||
fn setup_cameras(mut cmd: Commands, mut global_state: ResMut<GlobalState>) {
|
fn setup(
|
||||||
global_state.setup_2d_cam(&mut cmd);
|
mut cmd: Commands,
|
||||||
global_state.setup_3d_cam(&mut cmd);
|
mut images: ResMut<Assets<Image>>,
|
||||||
|
mut egui_user_textures: ResMut<EguiUserTextures>,
|
||||||
|
) {
|
||||||
|
let offscreen_image_size = Vec2::new(256.0, 256.0);
|
||||||
|
|
||||||
|
let size = Extent3d {
|
||||||
|
width: offscreen_image_size.x as u32,
|
||||||
|
height: offscreen_image_size.y as u32,
|
||||||
|
..default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the texture that will be rendered to.
|
||||||
|
let mut image = Image {
|
||||||
|
texture_descriptor: TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size,
|
||||||
|
dimension: TextureDimension::D2,
|
||||||
|
format: TextureFormat::Bgra8UnormSrgb,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
usage: TextureUsages::TEXTURE_BINDING
|
||||||
|
| TextureUsages::COPY_DST
|
||||||
|
| TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
view_formats: &[],
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// fill image.data with zeroes
|
||||||
|
image.resize(size);
|
||||||
|
|
||||||
|
let image_handle = images.add(image);
|
||||||
|
egui_user_textures.add_image(image_handle.clone());
|
||||||
|
|
||||||
|
// This specifies the layer used for the offscreen image, which will be attached to the first pass camera.
|
||||||
|
let offscreen_render_layer = RenderLayers::layer(1);
|
||||||
|
|
||||||
|
let camera_2d = cmd.spawn(Camera2dBundle::default()).id();
|
||||||
|
|
||||||
|
let camera_3d = cmd
|
||||||
|
.spawn((
|
||||||
|
Camera3dBundle {
|
||||||
|
camera: Camera {
|
||||||
|
target: RenderTarget::Image(image_handle.clone()),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
transform: Transform::from_xyz(200.0, -600.0, 600.0)
|
||||||
|
.looking_at(Vec3::ZERO, Vec3::Z),
|
||||||
|
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
offscreen_render_layer,
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
|
||||||
|
cmd.insert_resource(GlobalState::new(
|
||||||
|
camera_2d,
|
||||||
|
camera_3d,
|
||||||
|
image_handle,
|
||||||
|
offscreen_image_size,
|
||||||
|
offscreen_render_layer,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
@ -43,44 +110,44 @@ fn update_ui_side_panel(
|
||||||
mut standard_materials: ResMut<Assets<StandardMaterial>>,
|
mut standard_materials: ResMut<Assets<StandardMaterial>>,
|
||||||
mut color_materials: ResMut<Assets<ColorMaterial>>,
|
mut color_materials: ResMut<Assets<ColorMaterial>>,
|
||||||
) {
|
) {
|
||||||
|
let offscreen_texture_id = contexts.image_id(&global_state.offscreen_image).unwrap();
|
||||||
|
|
||||||
egui::SidePanel::right("2D View")
|
egui::SidePanel::right("2D View")
|
||||||
.default_width(400.0)
|
.default_width(400.0)
|
||||||
.show(contexts.ctx_mut(), |ui| {
|
.show(contexts.ctx_mut(), |ui| {
|
||||||
|
ui.add(egui::widgets::Image::new(
|
||||||
|
offscreen_texture_id,
|
||||||
|
bevy_egui::egui::Vec2::new(
|
||||||
|
global_state.offscreen_image_size.x,
|
||||||
|
global_state.offscreen_image_size.y,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
if global_state.view_2d {
|
if global_state.view_2d {
|
||||||
if ui.button("Change to 3D View").clicked() {
|
if ui.button("Change to 3D View").clicked() {
|
||||||
global_state.view_2d = false;
|
global_state.view_2d = false;
|
||||||
|
|
||||||
object_infos.iter_mut().for_each(|polygon| {
|
// object_infos.iter_mut().for_each(|polygon| {
|
||||||
polygon.swap_to_3d(
|
// polygon.swap_to_3d(
|
||||||
&global_state,
|
// &global_state,
|
||||||
&mut commands,
|
// &mut commands,
|
||||||
&mut meshes,
|
// &mut meshes,
|
||||||
&mut standard_materials,
|
// &mut standard_materials,
|
||||||
)
|
// )
|
||||||
});
|
// });
|
||||||
|
|
||||||
global_state.setup_3d_cam(&mut commands);
|
|
||||||
|
|
||||||
debug_assert!(global_state.camera.is_some());
|
|
||||||
debug_assert!(global_state.camera.as_ref().unwrap().is_3d());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ui.button("Change to 2D View").clicked() {
|
if ui.button("Change to 2D View").clicked() {
|
||||||
global_state.view_2d = true;
|
global_state.view_2d = true;
|
||||||
|
|
||||||
object_infos.iter_mut().for_each(|polygon| {
|
// object_infos.iter_mut().for_each(|polygon| {
|
||||||
polygon.swap_to_2d(
|
// polygon.swap_to_2d(
|
||||||
&global_state,
|
// &global_state,
|
||||||
&mut commands,
|
// &mut commands,
|
||||||
&mut meshes,
|
// &mut meshes,
|
||||||
&mut color_materials,
|
// &mut color_materials,
|
||||||
)
|
// )
|
||||||
});
|
// });
|
||||||
|
|
||||||
global_state.setup_2d_cam(&mut commands);
|
|
||||||
|
|
||||||
debug_assert!(global_state.camera.is_some());
|
|
||||||
debug_assert!(global_state.camera.as_ref().unwrap().is_2d());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +176,8 @@ fn mouse_event_system(
|
||||||
mut object_infos: ResMut<ObjectInfos>,
|
mut object_infos: ResMut<ObjectInfos>,
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
mut color_materials: ResMut<Assets<ColorMaterial>>,
|
||||||
|
mut standard_materials: ResMut<Assets<StandardMaterial>>,
|
||||||
) {
|
) {
|
||||||
for event in mouse_button_input_events.iter() {
|
for event in mouse_button_input_events.iter() {
|
||||||
match event.button {
|
match event.button {
|
||||||
|
@ -126,7 +194,8 @@ fn mouse_event_system(
|
||||||
&global_state,
|
&global_state,
|
||||||
&mut commands,
|
&mut commands,
|
||||||
&mut meshes,
|
&mut meshes,
|
||||||
&mut materials,
|
&mut color_materials,
|
||||||
|
&mut standard_materials,
|
||||||
) {
|
) {
|
||||||
global_state.polygon_started = false;
|
global_state.polygon_started = false;
|
||||||
}
|
}
|
||||||
|
@ -153,9 +222,15 @@ fn mouse_event_system(
|
||||||
fn on_resize_system(
|
fn on_resize_system(
|
||||||
mut resize_reader: EventReader<WindowResized>,
|
mut resize_reader: EventReader<WindowResized>,
|
||||||
mut global_state: ResMut<GlobalState>,
|
mut global_state: ResMut<GlobalState>,
|
||||||
|
mut camera: Query<&mut Transform, &Camera3d>,
|
||||||
) {
|
) {
|
||||||
for e in resize_reader.iter() {
|
for e in resize_reader.iter() {
|
||||||
// When resolution is being changed
|
// When resolution is being changed
|
||||||
global_state.window_extent = Vec2::new(e.width, e.height);
|
global_state.window_extent = Vec2::new(e.width, e.height);
|
||||||
|
|
||||||
|
for mut transform in &mut camera {
|
||||||
|
*transform = Transform::from_xyz(200.0, -600.0, 600.0)
|
||||||
|
.looking_at((global_state.window_extent * 0.5).extend(0.0), Vec3::Z);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
451
src/polygon.rs
451
src/polygon.rs
|
@ -14,10 +14,16 @@ struct CollectionIDs {
|
||||||
lines: Vec<Entity>,
|
lines: Vec<Entity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct FinishedIDs {
|
||||||
|
plane: Entity,
|
||||||
|
object: Entity,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum PolygonState {
|
enum PolygonState {
|
||||||
Collection(CollectionIDs),
|
Collection(CollectionIDs),
|
||||||
Finished(Entity),
|
Finished(FinishedIDs),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PolygonState {
|
impl PolygonState {
|
||||||
|
@ -35,84 +41,234 @@ impl Default for PolygonState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
enum PolygonView {
|
|
||||||
TwoDimensional(PolygonState),
|
|
||||||
ThreeDimensional(Option<Entity>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PolygonView {
|
|
||||||
fn is_2d(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
PolygonView::TwoDimensional(_) => true,
|
|
||||||
PolygonView::ThreeDimensional(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_3d(&self) -> bool {
|
|
||||||
!self.is_2d()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn two_dimensional_mut(&mut self) -> &mut PolygonState {
|
|
||||||
match self {
|
|
||||||
PolygonView::TwoDimensional(t) => t,
|
|
||||||
PolygonView::ThreeDimensional(_) => panic!("called 2d on 3d!"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn three_dimensional_mut(&mut self) -> &mut Option<Entity> {
|
|
||||||
match self {
|
|
||||||
PolygonView::TwoDimensional(_) => panic!("called 3d on 2d!"),
|
|
||||||
PolygonView::ThreeDimensional(t) => t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for PolygonView {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::TwoDimensional(PolygonState::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq)]
|
#[derive(Default, Debug, PartialEq)]
|
||||||
pub struct Polygon {
|
pub struct Polygon {
|
||||||
points: Vec<Vec2>,
|
points: Vec<Vec2>,
|
||||||
state: PolygonView,
|
state: PolygonState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Polygon {
|
impl Polygon {
|
||||||
const PROXIMITY_THRESHOLD: f32 = 10.0;
|
const PROXIMITY_THRESHOLD: f32 = 10.0;
|
||||||
|
|
||||||
pub fn swap_to_3d(
|
// pub fn swap_to_3d(
|
||||||
|
// &mut self,
|
||||||
|
// global_state: &GlobalState,
|
||||||
|
// commands: &mut Commands,
|
||||||
|
// meshes: &mut ResMut<Assets<Mesh>>,
|
||||||
|
// materials: &mut ResMut<Assets<StandardMaterial>>,
|
||||||
|
// ) {
|
||||||
|
// debug_assert!(self.state.is_2d());
|
||||||
|
|
||||||
|
// // clear all 2d entities
|
||||||
|
// let finished = match self.state.two_dimensional_mut() {
|
||||||
|
// PolygonState::Collection(collection_ids) => {
|
||||||
|
// for point in collection_ids.points.iter() {
|
||||||
|
// commands.entity(*point).despawn();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for line in collection_ids.lines.iter() {
|
||||||
|
// commands.entity(*line).despawn();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// false
|
||||||
|
// }
|
||||||
|
// PolygonState::Finished(mesh) => {
|
||||||
|
// commands.entity(*mesh).despawn();
|
||||||
|
|
||||||
|
// true
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
// self.state = PolygonView::ThreeDimensional(if finished {
|
||||||
|
// let vertices: Vec<Vec3> = self
|
||||||
|
// .points
|
||||||
|
// .iter()
|
||||||
|
// .map(|p| vec![p.extend(0.0), p.extend(global_state.extrusion_height)])
|
||||||
|
// .flatten()
|
||||||
|
// .collect();
|
||||||
|
|
||||||
|
// let indices = Indices::U32({
|
||||||
|
// let mut v = Vec::new();
|
||||||
|
|
||||||
|
// for i in 0..self.points.len() {
|
||||||
|
// let index = (i as u32) * 2;
|
||||||
|
|
||||||
|
// // create quads from 4 adjacent points
|
||||||
|
// v.push(index);
|
||||||
|
// v.push((index + 2) % vertices.len() as u32);
|
||||||
|
// v.push(index + 1);
|
||||||
|
// v.push(index + 1);
|
||||||
|
// v.push((index + 2) % vertices.len() as u32);
|
||||||
|
// v.push((index + 3) % vertices.len() as u32);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // TODO: proper ear cutting algo
|
||||||
|
// for i in 2..self.points.len() as u32 {
|
||||||
|
// let index = (i as u32) * 2 + 1;
|
||||||
|
|
||||||
|
// v.push(1);
|
||||||
|
// v.push(index - 2);
|
||||||
|
// v.push(index);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// v
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
|
||||||
|
// mesh.set_indices(Some(indices));
|
||||||
|
// mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
|
||||||
|
|
||||||
|
// let mesh_id = commands
|
||||||
|
// .spawn(MaterialMeshBundle {
|
||||||
|
// mesh: meshes.add(mesh).into(),
|
||||||
|
// transform: Transform::default(),
|
||||||
|
// material: materials.add(StandardMaterial::from(Color::BLACK)),
|
||||||
|
// ..default()
|
||||||
|
// })
|
||||||
|
// .id();
|
||||||
|
|
||||||
|
// Some(mesh_id)
|
||||||
|
// } else {
|
||||||
|
// None
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub fn swap_to_2d(
|
||||||
|
// &mut self,
|
||||||
|
// global_state: &GlobalState,
|
||||||
|
// commands: &mut Commands,
|
||||||
|
// meshes: &mut ResMut<Assets<Mesh>>,
|
||||||
|
// materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||||
|
// ) {
|
||||||
|
// debug_assert!(self.state.is_3d());
|
||||||
|
|
||||||
|
// // clear 3d entity
|
||||||
|
|
||||||
|
// let finished = match self.state.three_dimensional_mut() {
|
||||||
|
// Some(mesh_id) => {
|
||||||
|
// commands.entity(*mesh_id).despawn();
|
||||||
|
|
||||||
|
// true
|
||||||
|
// }
|
||||||
|
// None => false,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// self.state = PolygonView::TwoDimensional(if finished {
|
||||||
|
// // create triangle mesh
|
||||||
|
// let triangle_mesh = self.create_triangulated_mesh();
|
||||||
|
|
||||||
|
// let triangle_id = commands
|
||||||
|
// .spawn(MaterialMesh2dBundle {
|
||||||
|
// mesh: meshes.add(triangle_mesh).into(),
|
||||||
|
// transform: Transform::default()
|
||||||
|
// .with_translation((-global_state.window_extent * 0.5).extend(0.0)),
|
||||||
|
// material: materials.add(ColorMaterial::from(Color::BLACK)),
|
||||||
|
// ..default()
|
||||||
|
// })
|
||||||
|
// .id();
|
||||||
|
|
||||||
|
// // store triangle entity id in state
|
||||||
|
// PolygonState::Finished(triangle_id)
|
||||||
|
// } else {
|
||||||
|
// // create point meshes for every point
|
||||||
|
// let point_ids = self
|
||||||
|
// .points
|
||||||
|
// .iter()
|
||||||
|
// .map(|&point| {
|
||||||
|
// commands
|
||||||
|
// .spawn(MaterialMesh2dBundle {
|
||||||
|
// mesh: meshes.add(Mesh::from(shape::Quad::default())).into(),
|
||||||
|
// transform: Transform::default()
|
||||||
|
// .with_scale(Vec3::splat(4.0))
|
||||||
|
// .with_translation(
|
||||||
|
// (point - (global_state.window_extent * 0.5)).extend(0.0),
|
||||||
|
// ),
|
||||||
|
// material: materials.add(ColorMaterial::from(Color::BLACK)),
|
||||||
|
// ..default()
|
||||||
|
// })
|
||||||
|
// .id()
|
||||||
|
// })
|
||||||
|
// .collect();
|
||||||
|
|
||||||
|
// // draw lines between the points
|
||||||
|
// let line_ids = (1..self.points.len())
|
||||||
|
// .map(|i| {
|
||||||
|
// let previous_point = self.points[i - 1];
|
||||||
|
// let point = self.points[i];
|
||||||
|
|
||||||
|
// let a = Vec2::X.angle_between((point - previous_point).normalize());
|
||||||
|
|
||||||
|
// commands
|
||||||
|
// .spawn(MaterialMesh2dBundle {
|
||||||
|
// mesh: meshes
|
||||||
|
// .add(Self::create_line_mesh(previous_point, point))
|
||||||
|
// .into(),
|
||||||
|
// transform: Transform::default()
|
||||||
|
// .with_translation(
|
||||||
|
// (previous_point - (global_state.window_extent * 0.5))
|
||||||
|
// .extend(0.0),
|
||||||
|
// )
|
||||||
|
// .with_rotation(Quat::from_rotation_z(a)),
|
||||||
|
// material: materials.add(ColorMaterial::from(Color::BLACK)),
|
||||||
|
// ..default()
|
||||||
|
// })
|
||||||
|
// .id()
|
||||||
|
// })
|
||||||
|
// .collect();
|
||||||
|
|
||||||
|
// PolygonState::Collection(CollectionIDs {
|
||||||
|
// points: point_ids,
|
||||||
|
// lines: line_ids,
|
||||||
|
// })
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn points(&self) -> &[Vec2] {
|
||||||
|
&self.points
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_point(
|
||||||
&mut self,
|
&mut self,
|
||||||
global_state: &GlobalState,
|
global_state: &GlobalState,
|
||||||
commands: &mut Commands,
|
commands: &mut Commands,
|
||||||
meshes: &mut ResMut<Assets<Mesh>>,
|
meshes: &mut ResMut<Assets<Mesh>>,
|
||||||
materials: &mut ResMut<Assets<StandardMaterial>>,
|
color_materials: &mut ResMut<Assets<ColorMaterial>>,
|
||||||
) {
|
standard_materials: &mut ResMut<Assets<StandardMaterial>>,
|
||||||
debug_assert!(self.state.is_2d());
|
) -> bool {
|
||||||
|
debug_assert_eq!(self.finished(), false);
|
||||||
|
|
||||||
// clear all 2d entities
|
let point = global_state.cursor_position;
|
||||||
let finished = match self.state.two_dimensional_mut() {
|
|
||||||
PolygonState::Collection(collection_ids) => {
|
// polygon must be at least an triangle
|
||||||
for point in collection_ids.points.iter() {
|
// and check if we can close up the polygon
|
||||||
commands.entity(*point).despawn();
|
if self.points.len() >= 3 && self.check_first_for_proximity(point) {
|
||||||
|
// remove points and lines from collection state
|
||||||
|
{
|
||||||
|
let collection_ids = self.state.collection_mut();
|
||||||
|
|
||||||
|
for point_id in collection_ids.points.iter() {
|
||||||
|
commands.entity(*point_id).despawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
for line in collection_ids.lines.iter() {
|
for line_id in collection_ids.lines.iter() {
|
||||||
commands.entity(*line).despawn();
|
commands.entity(*line_id).despawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
PolygonState::Finished(mesh) => {
|
|
||||||
commands.entity(*mesh).despawn();
|
|
||||||
|
|
||||||
true
|
// create triangle mesh
|
||||||
}
|
let triangle_mesh = self.create_triangulated_mesh();
|
||||||
};
|
|
||||||
|
|
||||||
self.state = PolygonView::ThreeDimensional(if finished {
|
let triangle_id = commands
|
||||||
|
.spawn(MaterialMesh2dBundle {
|
||||||
|
mesh: meshes.add(triangle_mesh).into(),
|
||||||
|
transform: Transform::default()
|
||||||
|
.with_translation((-global_state.window_extent * 0.5).extend(0.0)),
|
||||||
|
material: color_materials.add(ColorMaterial::from(Color::BLACK)),
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.id();
|
||||||
|
|
||||||
|
// create 3d object
|
||||||
let vertices: Vec<Vec3> = self
|
let vertices: Vec<Vec3> = self
|
||||||
.points
|
.points
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -152,163 +308,29 @@ impl Polygon {
|
||||||
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
|
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
|
||||||
|
|
||||||
let mesh_id = commands
|
let mesh_id = commands
|
||||||
.spawn(MaterialMeshBundle {
|
.spawn((
|
||||||
mesh: meshes.add(mesh).into(),
|
MaterialMeshBundle {
|
||||||
transform: Transform::default(),
|
mesh: meshes.add(mesh).into(),
|
||||||
material: materials.add(StandardMaterial::from(Color::BLACK)),
|
transform: Transform::default(),
|
||||||
..default()
|
material: standard_materials.add(StandardMaterial::from(Color::BLACK)),
|
||||||
})
|
..default()
|
||||||
|
},
|
||||||
|
global_state.offscreen_render_layer,
|
||||||
|
))
|
||||||
.id();
|
.id();
|
||||||
|
|
||||||
Some(mesh_id)
|
// store finished entities in state
|
||||||
} else {
|
self.state = PolygonState::Finished(FinishedIDs {
|
||||||
None
|
plane: triangle_id,
|
||||||
});
|
object: mesh_id,
|
||||||
}
|
});
|
||||||
|
|
||||||
pub fn swap_to_2d(
|
|
||||||
&mut self,
|
|
||||||
global_state: &GlobalState,
|
|
||||||
commands: &mut Commands,
|
|
||||||
meshes: &mut ResMut<Assets<Mesh>>,
|
|
||||||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
|
||||||
) {
|
|
||||||
debug_assert!(self.state.is_3d());
|
|
||||||
|
|
||||||
// clear 3d entity
|
|
||||||
|
|
||||||
let finished = match self.state.three_dimensional_mut() {
|
|
||||||
Some(mesh_id) => {
|
|
||||||
commands.entity(*mesh_id).despawn();
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
None => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.state = PolygonView::TwoDimensional(if finished {
|
|
||||||
// create triangle mesh
|
|
||||||
let triangle_mesh = self.create_triangulated_mesh();
|
|
||||||
|
|
||||||
let triangle_id = commands
|
|
||||||
.spawn(MaterialMesh2dBundle {
|
|
||||||
mesh: meshes.add(triangle_mesh).into(),
|
|
||||||
transform: Transform::default()
|
|
||||||
.with_translation((-global_state.window_extent * 0.5).extend(0.0)),
|
|
||||||
material: materials.add(ColorMaterial::from(Color::BLACK)),
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.id();
|
|
||||||
|
|
||||||
// store triangle entity id in state
|
|
||||||
PolygonState::Finished(triangle_id)
|
|
||||||
} else {
|
|
||||||
// create point meshes for every point
|
|
||||||
let point_ids = self
|
|
||||||
.points
|
|
||||||
.iter()
|
|
||||||
.map(|&point| {
|
|
||||||
commands
|
|
||||||
.spawn(MaterialMesh2dBundle {
|
|
||||||
mesh: meshes.add(Mesh::from(shape::Quad::default())).into(),
|
|
||||||
transform: Transform::default()
|
|
||||||
.with_scale(Vec3::splat(4.0))
|
|
||||||
.with_translation(
|
|
||||||
(point - (global_state.window_extent * 0.5)).extend(0.0),
|
|
||||||
),
|
|
||||||
material: materials.add(ColorMaterial::from(Color::BLACK)),
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.id()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// draw lines between the points
|
|
||||||
let line_ids = (1..self.points.len())
|
|
||||||
.map(|i| {
|
|
||||||
let previous_point = self.points[i - 1];
|
|
||||||
let point = self.points[i];
|
|
||||||
|
|
||||||
let a = Vec2::X.angle_between((point - previous_point).normalize());
|
|
||||||
|
|
||||||
commands
|
|
||||||
.spawn(MaterialMesh2dBundle {
|
|
||||||
mesh: meshes
|
|
||||||
.add(Self::create_line_mesh(previous_point, point))
|
|
||||||
.into(),
|
|
||||||
transform: Transform::default()
|
|
||||||
.with_translation(
|
|
||||||
(previous_point - (global_state.window_extent * 0.5))
|
|
||||||
.extend(0.0),
|
|
||||||
)
|
|
||||||
.with_rotation(Quat::from_rotation_z(a)),
|
|
||||||
material: materials.add(ColorMaterial::from(Color::BLACK)),
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.id()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
PolygonState::Collection(CollectionIDs {
|
|
||||||
points: point_ids,
|
|
||||||
lines: line_ids,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn points(&self) -> &[Vec2] {
|
|
||||||
&self.points
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_point(
|
|
||||||
&mut self,
|
|
||||||
global_state: &GlobalState,
|
|
||||||
commands: &mut Commands,
|
|
||||||
meshes: &mut ResMut<Assets<Mesh>>,
|
|
||||||
materials: &mut ResMut<Assets<ColorMaterial>>,
|
|
||||||
) -> bool {
|
|
||||||
debug_assert_eq!(self.finished(), false);
|
|
||||||
|
|
||||||
let point = global_state.cursor_position;
|
|
||||||
|
|
||||||
// polygon must be at least an triangle
|
|
||||||
// and check if we can close up the polygon
|
|
||||||
if self.points.len() >= 3 && self.check_first_for_proximity(point) {
|
|
||||||
// remove points and lines from collection state
|
|
||||||
{
|
|
||||||
let collection_ids = self.state.two_dimensional_mut().collection_mut();
|
|
||||||
|
|
||||||
for point_id in collection_ids.points.iter() {
|
|
||||||
commands.entity(*point_id).despawn();
|
|
||||||
}
|
|
||||||
|
|
||||||
for line_id in collection_ids.lines.iter() {
|
|
||||||
commands.entity(*line_id).despawn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create triangle mesh
|
|
||||||
let triangle_mesh = self.create_triangulated_mesh();
|
|
||||||
|
|
||||||
let triangle_id = commands
|
|
||||||
.spawn(MaterialMesh2dBundle {
|
|
||||||
mesh: meshes.add(triangle_mesh).into(),
|
|
||||||
transform: Transform::default()
|
|
||||||
.with_translation((-global_state.window_extent * 0.5).extend(0.0)),
|
|
||||||
material: materials.add(ColorMaterial::from(Color::BLACK)),
|
|
||||||
..default()
|
|
||||||
})
|
|
||||||
.id();
|
|
||||||
|
|
||||||
// store triangle entity id in state
|
|
||||||
*self.state.two_dimensional_mut() = PolygonState::Finished(triangle_id);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// deny too close points
|
// deny too close points
|
||||||
if !self.check_all_for_proximity(point) {
|
if !self.check_all_for_proximity(point) {
|
||||||
let collection_ids = self.state.two_dimensional_mut().collection_mut();
|
let collection_ids = self.state.collection_mut();
|
||||||
|
|
||||||
// create point mesh at click position
|
// create point mesh at click position
|
||||||
let point_id = commands
|
let point_id = commands
|
||||||
|
@ -320,7 +342,7 @@ impl Polygon {
|
||||||
(global_state.cursor_position - (global_state.window_extent * 0.5))
|
(global_state.cursor_position - (global_state.window_extent * 0.5))
|
||||||
.extend(0.0),
|
.extend(0.0),
|
||||||
),
|
),
|
||||||
material: materials.add(ColorMaterial::from(Color::BLACK)),
|
material: color_materials.add(ColorMaterial::from(Color::BLACK)),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.id();
|
.id();
|
||||||
|
@ -339,7 +361,7 @@ impl Polygon {
|
||||||
(last_point - (global_state.window_extent * 0.5)).extend(0.0),
|
(last_point - (global_state.window_extent * 0.5)).extend(0.0),
|
||||||
)
|
)
|
||||||
.with_rotation(Quat::from_rotation_z(a)),
|
.with_rotation(Quat::from_rotation_z(a)),
|
||||||
material: materials.add(ColorMaterial::from(Color::BLACK)),
|
material: color_materials.add(ColorMaterial::from(Color::BLACK)),
|
||||||
..default()
|
..default()
|
||||||
})
|
})
|
||||||
.id();
|
.id();
|
||||||
|
@ -355,11 +377,8 @@ impl Polygon {
|
||||||
|
|
||||||
pub fn finished(&self) -> bool {
|
pub fn finished(&self) -> bool {
|
||||||
match &self.state {
|
match &self.state {
|
||||||
PolygonView::TwoDimensional(t) => match t {
|
PolygonState::Collection(_) => false,
|
||||||
PolygonState::Collection(_) => false,
|
PolygonState::Finished(_) => true,
|
||||||
PolygonState::Finished(_) => true,
|
|
||||||
},
|
|
||||||
PolygonView::ThreeDimensional(_) => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue