cyborg/editor/src/world.rs

192 lines
5.3 KiB
Rust

use legion::Entity;
use crate::model;
use std::ops::{Deref, DerefMut};
pub struct World {
pub world: legion::World,
pub resources: legion::Resources,
pub root_objects: Vec<Entity>,
}
impl Deref for World {
type Target = legion::World;
fn deref(&self) -> &legion::World {
&self.world
}
}
impl DerefMut for World {
fn deref_mut(&mut self) -> &mut legion::World {
&mut self.world
}
}
impl World {
pub fn new() -> Self {
Self {
world: Default::default(),
resources: Default::default(),
root_objects: vec![],
}
}
pub fn execute(&mut self, schedule: &mut legion::Schedule) {
schedule.execute(&mut self.world, &mut self.resources);
}
pub fn get_component_mut<T: 'static + Send + Sync>(&mut self, entity: Entity) -> &mut T {
self.entry(entity).unwrap().into_component_mut().unwrap()
}
pub fn spawn_model(&mut self, model: &model::LoadedModel, transform: &glam::Mat4) -> Entity {
let mut children = Vec::new();
for object in model.objects.iter() {
children.push(self.spawn_object(object, transform));
}
let root = self.world.push((Object {
name: model.name.to_owned(),
transform: model::Transform::default(),
entities: Default::default(),
children,
dirty: false,
children_dirty: false,
},));
self.root_objects.push(root);
root
}
pub fn spawn_object(
&mut self,
object: &model::LoadedObject,
root_transform: &glam::Mat4,
) -> Entity {
let transform = cyborg::scene::Transform {
transform: *root_transform * object.transform.to_mat4(),
};
let mut entities = Vec::new();
for mesh in object.meshes.iter() {
let mesh = cyborg::scene::Mesh { mesh: *mesh };
let entity = self.world.push((mesh, transform.clone()));
entities.push(entity);
}
let mut children = Vec::new();
for child in object.children.iter() {
children.push(self.spawn_object(child, root_transform));
}
self.world.push((Object {
name: object.name.clone(),
transform: object.transform.clone(),
entities,
children,
dirty: false,
children_dirty: false,
},))
}
pub fn flush_dirty_objects(&mut self) {
let mut stack = self.root_objects.to_owned();
while let Some(parent) = stack.pop() {
let parent: &mut Object = self.get_component_mut(parent);
if parent.children_dirty {
parent.children_dirty = false;
stack.extend_from_slice(&parent.children);
}
if parent.dirty {
parent.dirty = false;
let mat4 = parent.transform.to_mat4();
for entity in parent.entities.to_owned().into_iter() {
let transform: &mut cyborg::scene::Transform = self.get_component_mut(entity);
transform.transform = mat4;
}
}
}
}
}
pub struct Object {
pub name: Option<String>,
pub transform: model::Transform,
pub entities: Vec<Entity>,
pub children: Vec<Entity>,
pub dirty: bool,
pub children_dirty: bool,
}
impl Object {
pub fn ui(&mut self, ui: &mut egui::Ui) {
egui::Grid::new("root_object")
.num_columns(4)
.striped(true)
.show(ui, |ui| {
ui.label("Position: ");
self.ui_position(ui);
ui.end_row();
ui.label("Rotation: ");
self.ui_rotation(ui);
ui.end_row();
ui.label("Scale: ");
self.ui_scale(ui);
ui.end_row();
});
}
pub fn ui_position(&mut self, ui: &mut egui::Ui) {
let speed = 0.1 * self.transform.scale.min_element();
if Self::ui_vec3(ui, speed, &mut self.transform.position) {
self.dirty = true;
}
}
pub fn ui_rotation(&mut self, ui: &mut egui::Ui) {
let axes = &mut self.transform.rotation;
if ui.drag_angle(&mut axes.x).changed() {
self.dirty = true;
}
if ui.drag_angle(&mut axes.y).changed() {
self.dirty = true;
}
if ui.drag_angle(&mut axes.z).changed() {
self.dirty = true;
}
}
pub fn ui_vec3(ui: &mut egui::Ui, speed: f32, vec3: &mut glam::Vec3) -> bool {
let x_drag = egui::DragValue::new(&mut vec3.x).speed(speed);
let dirty = ui.add(x_drag).changed();
let dirty = dirty || {
// For some reason, Rust complains if this isn't in a block
let y_drag = egui::DragValue::new(&mut vec3.y).speed(speed);
ui.add(y_drag).changed()
};
let z_drag = egui::DragValue::new(&mut vec3.z).speed(speed);
let dirty = dirty || ui.add(z_drag).changed();
dirty
}
pub fn ui_scale(&mut self, ui: &mut egui::Ui) {
let speed = self.transform.scale.min_element() * 0.01;
if Self::ui_vec3(ui, speed, &mut self.transform.scale) {
self.dirty = true;
}
}
}