Add Model struct
This commit is contained in:
parent
c43544cfdf
commit
c5eb2bff49
|
@ -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![],
|
||||
}],
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue