Add Model struct

This commit is contained in:
mars 2022-05-18 20:00:02 -06:00
parent c43544cfdf
commit c5eb2bff49
5 changed files with 238 additions and 118 deletions

38
editor/src/import.rs Normal file
View File

@ -0,0 +1,38 @@
use crate::model;
pub fn load_stl(path: std::path::PathBuf) -> model::Model {
let name = path.file_name().map(|v| v.to_string_lossy().to_string());
let mut file = std::fs::File::open(path).unwrap();
let stl = stl::read_stl(&mut file).unwrap();
let mut vertices = Vec::new();
let mut indices = Vec::new();
for tri in stl.triangles.iter() {
indices.push(vertices.len() as u32);
vertices.push(model::BasicVertex {
position: tri.v1.into(),
});
indices.push(vertices.len() as u32);
vertices.push(model::BasicVertex {
position: tri.v2.into(),
});
indices.push(vertices.len() as u32);
vertices.push(model::BasicVertex {
position: tri.v3.into(),
});
}
model::Model {
name: name.clone(),
objects: vec![model::Object {
name,
mesh: model::Mesh {
transform: Default::default(),
data: model::MeshData { vertices, indices },
},
children: vec![],
}],
}
}

View File

@ -10,6 +10,8 @@ use egui_winit::winit::{
use legion::EntityStore;
use std::sync::Arc;
mod import;
mod model;
mod render;
mod ui;
@ -107,7 +109,9 @@ impl Application {
Ok(ui::FileEvent::SaveAs(path)) => println!("Saving as: {:?}", path),
Ok(ui::FileEvent::Import(kind, path)) => match kind {
ui::ImportKind::Stl => {
self.load_stl(path);
let model = import::load_stl(path);
let widgets = self.render_state.load_model(&model);
self.objects.extend(widgets.into_iter());
}
},
Err(crossbeam_channel::TryRecvError::Empty) => break,
@ -119,16 +123,7 @@ impl Application {
for object in self.objects.iter_mut() {
object.flush_dirty(|object| {
let translation = glam::Vec3::from_slice(&object.position);
let rotation = glam::Quat::from_euler(
glam::EulerRot::XYZ,
object.rotation[0],
object.rotation[1],
object.rotation[2],
);
let scale = glam::Vec3::splat(object.scale);
let transform =
glam::Mat4::from_scale_rotation_translation(scale, rotation, translation);
let transform = object.transform.to_mat4();
self.render_state
.world
.entry_mut(object.entity)
@ -140,89 +135,6 @@ impl Application {
}
}
pub fn load_stl(&mut self, path: std::path::PathBuf) {
let root_name = path
.file_name()
.map(|v| v.to_string_lossy().to_string())
.unwrap_or("<unnamed>".to_string());
use cyborg::pass::{
mesh::{Index, MeshPass, Vertex},
RenderPassBox,
};
use cyborg::storage::mesh::{AttrBuffer, MeshBuffer};
eprintln!("loading {:?}", path);
let mut file = std::fs::File::open(path).unwrap();
let stl = stl::read_stl(&mut file).unwrap();
let mut vertices = Vec::new();
let mut indices = Vec::new();
for tri in stl.triangles.iter() {
let tan_frame = 0; // TODO make from normal
indices.push(vertices.len() as Index);
vertices.push(Vertex {
position: tri.v1,
tan_frame,
});
indices.push(vertices.len() as Index);
vertices.push(Vertex {
position: tri.v2,
tan_frame,
});
indices.push(vertices.len() as Index);
vertices.push(Vertex {
position: tri.v3,
tan_frame,
});
}
let mesh_pass = self
.render_state
.resources
.get::<RenderPassBox<MeshPass>>()
.unwrap();
let attributes = mesh_pass.get_attributes();
let vertices = AttrBuffer {
id: attributes.vertex,
count: vertices.len(),
data: bytemuck::cast_slice(&vertices).to_vec(),
};
let indices = AttrBuffer {
id: attributes.index,
count: indices.len(),
data: bytemuck::cast_slice(&indices).to_vec(),
};
let mut mesh = MeshBuffer::default();
mesh.attributes.push(vertices);
mesh.attributes.push(indices);
let entity = self.render_state.world.push((
cyborg::scene::Mesh {
mesh: mesh_pass.get_mesh_pool().load(mesh).unwrap(),
},
cyborg::scene::Transform {
transform: Default::default(),
},
));
let object = ui::ObjectWidget {
name: root_name,
position: [0.0; 3],
rotation: [0.0; 3],
scale: 1.0,
entity,
dirty: false,
};
self.objects.push(object);
}
pub fn on_resize(&mut self, new_size: PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.size = new_size;

55
editor/src/model.rs Normal file
View File

@ -0,0 +1,55 @@
pub struct Model {
pub name: Option<String>,
pub objects: Vec<Object>,
}
pub struct Object {
pub name: Option<String>,
pub mesh: Mesh,
pub children: Vec<Object>,
}
pub struct Mesh {
pub transform: Transform,
pub data: MeshData,
}
pub struct MeshData {
pub vertices: Vec<BasicVertex>,
pub indices: Vec<u32>,
}
pub struct BasicVertex {
pub position: glam::Vec3,
}
#[derive(Copy, Clone, Debug)]
pub struct Transform {
pub position: glam::Vec3,
pub rotation: glam::Vec3, // TODO support glam::Quat too
pub scale: f32,
}
impl Transform {
pub fn to_mat4(&self) -> glam::Mat4 {
let translation = self.position;
let rotation = glam::Quat::from_euler(
glam::EulerRot::XYZ,
self.rotation[0],
self.rotation[1],
self.rotation[2],
);
let scale = glam::Vec3::splat(self.scale);
glam::Mat4::from_scale_rotation_translation(scale, rotation, translation)
}
}
impl Default for Transform {
fn default() -> Self {
Self {
position: glam::Vec3::ZERO,
rotation: glam::Vec3::ZERO,
scale: 1.0,
}
}
}

View File

@ -1,6 +1,8 @@
use crate::ui::ViewportWidget;
use crate::model;
use crate::ui::{ObjectWidget, ViewportWidget};
use crate::wgpu;
use cyborg::camera::Camera;
use cyborg::storage::mesh::MeshHandle;
use cyborg::viewport::{Viewport, ViewportInfo, ViewportViews};
use std::sync::Arc;
@ -258,4 +260,77 @@ impl RenderState {
self.render_schedule
.execute(&mut self.world, &mut self.resources);
}
pub fn load_model(&mut self, model: &model::Model) -> Vec<ObjectWidget> {
// TODO use model name?
let mut objects = Vec::new();
for object in model.objects.iter() {
objects.push(self.load_object(object));
}
objects
}
pub fn load_object(&mut self, object: &model::Object) -> ObjectWidget {
let mesh = self.load_mesh_data(&object.mesh.data);
let entity = self.world.push((
cyborg::scene::Mesh { mesh },
cyborg::scene::Transform {
transform: object.mesh.transform.to_mat4(),
},
));
let mut children = Vec::new();
for child in object.children.iter() {
children.push(self.load_object(child));
}
ObjectWidget {
name: object.name.clone(),
transform: object.mesh.transform.clone(),
entity,
children,
dirty: false,
children_dirty: false,
}
}
pub fn load_mesh_data(&mut self, data: &model::MeshData) -> MeshHandle {
use cyborg::pass::{
mesh::{MeshPass, Vertex},
RenderPassBox,
};
use cyborg::storage::mesh::{AttrBuffer, MeshBuffer};
let mesh_pass = self.resources.get::<RenderPassBox<MeshPass>>().unwrap();
let attributes = mesh_pass.get_attributes();
let vertices: Vec<_> = data
.vertices
.iter()
.map(|v| Vertex {
position: v.position.to_array(),
tan_frame: 0, // TODO encode tangents
})
.collect();
let vertices = AttrBuffer {
id: attributes.vertex,
count: vertices.len(),
data: bytemuck::cast_slice(&vertices).to_vec(),
};
let indices = AttrBuffer {
id: attributes.index,
count: data.indices.len(),
data: bytemuck::cast_slice(&data.indices).to_vec(),
};
let mut mesh = MeshBuffer::default();
mesh.attributes.push(vertices);
mesh.attributes.push(indices);
mesh_pass.get_mesh_pool().load(mesh).unwrap()
}
}

View File

@ -202,8 +202,8 @@ impl UserInterface {
egui::SidePanel::left("objects_panel")
.resizable(true)
.show(ctx, |ui| {
for object in objects.iter_mut() {
object.ui(ui);
for (index, object) in objects.iter_mut().enumerate() {
object.ui(index, ui);
}
});
@ -299,20 +299,28 @@ impl egui::Widget for &mut ViewportWidget {
}
pub struct ObjectWidget {
pub name: String,
pub position: [f32; 3],
pub rotation: [f32; 3],
pub scale: f32,
pub name: Option<String>,
pub transform: crate::model::Transform,
pub entity: legion::Entity,
pub children: Vec<ObjectWidget>,
pub dirty: bool,
pub children_dirty: bool,
}
impl ObjectWidget {
pub fn ui(&mut self, ui: &mut egui::Ui) {
egui::CollapsingHeader::new(&self.name)
pub fn ui(&mut self, index: usize, ui: &mut egui::Ui) {
egui::CollapsingHeader::new(self.name.as_ref().unwrap_or(&"<unnamed>".into()))
.id_source(format!("child_{}", index))
.default_open(true)
.show(ui, |ui| {
self.ui_self(ui);
for (index, child) in self.children.iter_mut().enumerate() {
child.ui(index, ui);
if child.dirty {
self.children_dirty = true;
}
}
});
}
@ -336,26 +344,47 @@ impl ObjectWidget {
}
pub fn ui_position(&mut self, ui: &mut egui::Ui) {
let speed = 0.1 * self.scale;
for axis in self.position.iter_mut() {
let drag = egui::DragValue::new(axis).speed(speed);
if ui.add(drag).changed() {
self.dirty = true;
}
let speed = 0.1 * self.transform.scale;
if Self::ui_vec3(ui, speed, &mut self.transform.position) {
self.dirty = true;
}
}
pub fn ui_rotation(&mut self, ui: &mut egui::Ui) {
for axis in self.rotation.iter_mut() {
if ui.drag_angle(axis).changed() {
self.dirty = true;
}
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 scale_speed = self.scale * 0.01;
let drag = egui::DragValue::new(&mut self.scale)
let scale_speed = self.transform.scale * 0.01;
let drag = egui::DragValue::new(&mut self.transform.scale)
.clamp_range(0.0..=f32::INFINITY)
.speed(scale_speed);
if ui.add(drag).changed() {
@ -364,9 +393,20 @@ impl ObjectWidget {
}
pub fn flush_dirty(&mut self, mut f: impl FnMut(&mut Self)) {
if self.dirty {
f(self);
self.dirty = false;
let mut stack = vec![self];
if let Some(parent) = stack.pop() {
if parent.dirty {
parent.dirty = false;
f(parent);
}
if parent.children_dirty {
parent.children_dirty = false;
for child in parent.children.iter_mut() {
stack.push(child);
}
}
}
}
}