Skip to content

Commit

Permalink
Proper pof import (#148)
Browse files Browse the repository at this point in the history
* nearly finished

* nearly finished

* fix up model path text edit

* move parent/child manipulation to pof crate

* review tweaks

* replace labeled blocks with .find() and .any()

* missed one

---------

Co-authored-by: Mario Carneiro <[email protected]>
  • Loading branch information
Baezon and digama0 authored Sep 29, 2023
1 parent a6c78ac commit 77325a4
Show file tree
Hide file tree
Showing 7 changed files with 1,602 additions and 75 deletions.
9 changes: 2 additions & 7 deletions pof/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,13 +479,6 @@ impl<R: Read + Seek> Parser<R> {
// now that all the subobjects shouldve have been slotted in, assert that they all exist
let mut sub_objects = ObjVec(sub_objects.into_iter().map(|subobj_opt| subobj_opt.unwrap()).collect());

for i in 0..sub_objects.len() {
if let Some(parent) = sub_objects.0[i].parent {
let id = sub_objects.0[i].obj_id;
sub_objects[parent].children.push(id);
}
}

debris_objs.retain(|id| {
if id.0 < sub_objects.len() as u32 {
sub_objects[*id].is_debris_model = true;
Expand Down Expand Up @@ -541,6 +534,8 @@ impl<R: Read + Seek> Parser<R> {
warnings: Default::default(),
errors: Default::default(),
};

model.recalc_all_children_ids();
model.recheck_warnings(Set::All);
model.recheck_errors(Set::All);
model.recalc_semantic_name_links();
Expand Down
121 changes: 74 additions & 47 deletions pof/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,10 @@ impl SpecialPoint {
self.position = matrix * self.position;
self.radius *= scalar;
}

pub fn is_subsystem(&self) -> bool {
properties_get_field(&self.properties, "$special") == Some("subsystem")
}
}

#[derive(Default, Debug, Clone)]
Expand Down Expand Up @@ -1269,6 +1273,12 @@ impl Serialize for ThrusterBank {
}
}

impl ThrusterBank {
pub fn get_engine_subsys(&self) -> Option<&str> {
properties_get_field(&self.properties, "$engine_subsystem")
}
}

macro_rules! mk_enumeration {
($($(#[$meta:meta])* pub enum $tyname:ident($base:ty) {
$($(#[$doc:meta])* $name:ident = $n:literal,)*
Expand Down Expand Up @@ -1397,7 +1407,7 @@ pub enum NameLink {
pub struct SubObject {
pub obj_id: ObjectId,
pub radius: f32,
pub(crate) parent: Option<ObjectId>,
pub parent: Option<ObjectId>,
pub offset: Vec3d,
pub geo_center: Vec3d,
pub bbox: BoundingBox,
Expand Down Expand Up @@ -1478,6 +1488,10 @@ impl SubObject {
pub fn uvec_fvec(&self) -> Option<(Vec3d, Vec3d)> {
parse_uvec_fvec(&self.properties)
}

pub fn is_subsystem(&self) -> bool {
properties_get_field(&self.properties, "$special") == Some("subsystem")
}
}

fn parse_uvec_fvec(props: &str) -> Option<(Vec3d, Vec3d)> {
Expand Down Expand Up @@ -1584,12 +1598,11 @@ impl Dock {
}

pub fn get_name(&self) -> Option<&str> {
for str in self.properties.split('\n') {
if let Some(name) = str.strip_prefix("$name=") {
return Some(name);
}
}
None
properties_get_field(&self.properties, "$name")
}

pub fn get_parent_obj(&self) -> Option<&str> {
properties_get_field(&self.properties, "$parent_submodel")
}

pub fn apply_transform(&mut self, matrix: &TMat4<f32>) {
Expand Down Expand Up @@ -1647,7 +1660,7 @@ impl Turret {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct Insignia {
pub detail_level: u32,
pub vertices: Vec<Vec3d>,
Expand Down Expand Up @@ -2206,11 +2219,12 @@ impl Model {
} else if sub_obj_parent.is_none() {
return false;
}
assert!(sub_obj_parent != self.sub_objects[sub_obj_parent.unwrap()].parent, "cycle detected!! {:?} {:?}", obj_id, sub_obj_parent);
sub_obj_parent = self.sub_objects[sub_obj_parent.unwrap()].parent;
}
}

pub fn get_detail_level(&self, obj_id: ObjectId) -> Option<u32> {
pub fn get_sobj_detail_level(&self, obj_id: ObjectId) -> Option<u32> {
for (i, id) in self.header.detail_levels.iter().enumerate() {
if self.is_obj_id_ancestor(obj_id, *id) {
return Some(i as u32);
Expand Down Expand Up @@ -2516,45 +2530,16 @@ impl Model {
}
}

pub fn clean_up(&mut self) {
if let Some(shield) = &mut self.shield_data {
if shield.collision_tree.is_none() {
shield.collision_tree = Some(ShieldData::recalculate_tree(&shield.verts, &shield.polygons));
}
}
}

pub fn make_orphan(&mut self, would_be_orphan: ObjectId) {
if let Some(parent_id) = self.sub_objects[would_be_orphan].parent {
// maintain it's current relative position to the whole model
self.sub_objects[would_be_orphan].offset = self.get_total_subobj_offset(would_be_orphan);

let parent_children = &mut self.sub_objects[parent_id].children;
parent_children.remove(parent_children.iter().position(|child_id| *child_id == would_be_orphan).unwrap());
}
self.sub_objects[would_be_orphan].parent = None;
}

pub fn make_parent(&mut self, new_parent: ObjectId, new_child: ObjectId) -> Option<()> {
if !self.is_obj_id_ancestor(new_parent, new_child) {
self.sub_objects[new_parent].children.push(new_child);
self.sub_objects[new_child].parent = Some(new_parent);

// maintain it's current relative position to the whole model
let offset_from_parents = self.get_total_subobj_offset(new_child) - self.sub_objects[new_child].offset;
self.sub_objects[new_child].offset -= offset_from_parents;

Some(())
} else {
None
pub fn recalc_all_children_ids(&mut self) {
for subobj in self.sub_objects.iter_mut() {
subobj.children.clear();
}
}

pub fn max_verts_norms_per_subobj(&self) -> usize {
if self.version >= Version::V23_00 {
u32::MAX as usize
} else {
u16::MAX as usize
for i in 0..self.sub_objects.len() {
if let Some(parent) = self.sub_objects.0[i].parent {
let id = self.sub_objects.0[i].obj_id;
self.sub_objects[parent].children.push(id);
}
}
}

Expand Down Expand Up @@ -2600,6 +2585,48 @@ impl Model {
}
}

pub fn clean_up(&mut self) {
if let Some(shield) = &mut self.shield_data {
if shield.collision_tree.is_none() {
shield.collision_tree = Some(ShieldData::recalculate_tree(&shield.verts, &shield.polygons));
}
}
}

pub fn make_orphan(&mut self, would_be_orphan: ObjectId) {
if let Some(parent_id) = self.sub_objects[would_be_orphan].parent {
// maintain it's current relative position to the whole model
self.sub_objects[would_be_orphan].offset = self.get_total_subobj_offset(would_be_orphan);

let parent_children = &mut self.sub_objects[parent_id].children;
parent_children.remove(parent_children.iter().position(|child_id| *child_id == would_be_orphan).unwrap());
}
self.sub_objects[would_be_orphan].parent = None;
}

pub fn make_parent(&mut self, new_parent: ObjectId, new_child: ObjectId) -> Option<()> {
if !self.is_obj_id_ancestor(new_parent, new_child) {
self.sub_objects[new_parent].children.push(new_child);
self.sub_objects[new_child].parent = Some(new_parent);

// maintain it's current relative position to the whole model
let offset_from_parents = self.get_total_subobj_offset(new_child) - self.sub_objects[new_child].offset;
self.sub_objects[new_child].offset -= offset_from_parents;

Some(())
} else {
None
}
}

pub fn max_verts_norms_per_subobj(&self) -> usize {
if self.version >= Version::V23_00 {
u32::MAX as usize
} else {
u16::MAX as usize
}
}

pub fn global_import(&mut self, mut import_model: Box<Model>) {
self.header.mass = import_model.header.mass;
self.header.moment_of_inertia = import_model.header.moment_of_inertia;
Expand Down Expand Up @@ -2730,7 +2757,7 @@ pub fn post_parse_fill_untextured_slot(sub_objects: &mut Vec<SubObject>, texture
}
}

fn properties_delete_field(properties: &mut String, field: &str) {
pub fn properties_delete_field(properties: &mut String, field: &str) {
if let Some(start_idx) = properties.find(field) {
let mut end_idx = if let Some(idx) = properties[start_idx..].chars().position(|d| d.is_ascii_control()) {
start_idx + idx
Expand Down
4 changes: 2 additions & 2 deletions pof/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,7 @@ impl Model {
let mut top_level_node = make_subobj_node(up, &self.sub_objects, subobj, &self.turrets, &mut geometries, &materials);

for (i, insignia) in self.insignias.iter().enumerate() {
if self.get_detail_level(subobj.obj_id) == Some(insignia.detail_level) {
if self.get_sobj_detail_level(subobj.obj_id) == Some(insignia.detail_level) {
top_level_node.children.push(make_insignia_node(insignia, &mut geometries, i, up))
}
}
Expand Down Expand Up @@ -1466,7 +1466,7 @@ impl GltfBuilder {
let mut top_level_node = self.make_subobj_node(&model.sub_objects, subobj, &model.turrets, model.textures.len());

for (i, insignia) in model.insignias.iter().enumerate() {
if model.get_detail_level(subobj.obj_id) == Some(insignia.detail_level) {
if model.get_sobj_detail_level(subobj.obj_id) == Some(insignia.detail_level) {
top_level_node.children().push(self.make_insignia_node(insignia, i, up))
}
}
Expand Down
45 changes: 44 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#![allow(clippy::useless_format)]
#![allow(clippy::explicit_auto_deref)]
#![allow(clippy::collapsible_if)]
#[macro_use]
extern crate log;
extern crate nalgebra_glm as glm;
Expand Down Expand Up @@ -36,12 +37,14 @@ use std::{
io::{Cursor, Read},
ops::{Deref, DerefMut},
path::PathBuf,
sync::mpsc::Receiver,
sync::mpsc::TryRecvError,
};
use ui::{PofToolsGui, TreeValue};

mod primitives;
mod ui;
mod ui_import;
mod ui_properties_panel;

fn create_display(event_loop: &glutin::event_loop::EventLoop<()>) -> glium::Display {
Expand Down Expand Up @@ -393,6 +396,13 @@ impl Model {
}
}

/// * `None`: If the thread is inactive
/// * `Some(Reciever)`: Otherwise if something is being loaded or the window is open. Reciever results are:
/// * `Ok(Some(Model))`: If the thread has successfuly loaded and returned a model
/// * `Ok(None)`: The loading was canceled, probably because they closed the window before choosing anything
/// * `Err(panic message)`: the loading failed! Probably while parsing the model
type LoadingThread = Option<Receiver<Result<Option<Box<Model>>, String>>>;

impl PofToolsGui {
fn save_model(model: &Model) -> Option<String> {
let mut out = None;
Expand Down Expand Up @@ -459,6 +469,25 @@ impl PofToolsGui {
model.map_err(|panic| *panic.downcast().unwrap())
}

/// same as `handle_model_loading_thread` but for the import model
fn handle_import_model_loading_thread(&mut self) {
if let Some(thread) = &self.import_window.import_model_loading_thread {
let response = thread.try_recv();
match response {
Ok(Ok(Some(data))) => {
self.import_window.model = Some(data);
self.import_window.import_model_loading_thread = None;
self.import_window.import_selection.clear();
}
Err(TryRecvError::Disconnected) => self.import_window.import_model_loading_thread = None,
Ok(Ok(None)) => self.import_window.import_model_loading_thread = None,
Ok(Err(_)) => self.import_window.import_model_loading_thread = None,

Err(TryRecvError::Empty) => {}
}
}
}

/// opens a thread which opens the dialog and starts parsing a model
fn start_loading_model(&mut self, filepath: Option<PathBuf>) {
let (sender, receiver) = std::sync::mpsc::channel();
Expand All @@ -468,6 +497,7 @@ impl PofToolsGui {
std::thread::spawn(move || drop(sender.send(Self::load_model(filepath))));
}

/// handles talking to the model loading thread, ending it when concluded
fn handle_model_loading_thread(&mut self, display: &Display) -> bool {
if let Some(thread) = &self.model_loading_thread {
let response = thread.try_recv();
Expand Down Expand Up @@ -514,6 +544,7 @@ impl PofToolsGui {
for i in 0..self.model.textures.len() {
self.model.texture_map.insert(TextureId(i as u32), TextureId(i as u32));
}
self.import_window = Default::default();
self.ui_state.tree_view_selection = Default::default();
self.ui_state.refresh_properties_panel(&self.model);
self.camera_heading = 2.7;
Expand Down Expand Up @@ -623,6 +654,16 @@ impl PofToolsGui {
}
}

/// same as `start_loading_model` but for the import model
/// borrow checker stuff makes this easier to do as a free function
pub fn start_loading_import_model(thread: &mut LoadingThread) {
let (sender, receiver) = std::sync::mpsc::channel();
*thread = Some(receiver);

// the model loading thread
std::thread::spawn(move || drop(sender.send(PofToolsGui::load_model(None))));
}

const POF_TOOLS_VERSION: &str = env!("CARGO_PKG_VERSION");

fn main() {
Expand Down Expand Up @@ -698,6 +739,8 @@ fn main() {

pt_gui.handle_texture_loading_thread(&display);

pt_gui.handle_import_model_loading_thread();

let repaint_after = egui.run(&display, |ctx| pt_gui.show_ui(ctx, &display, &mut undo_history));

*control_flow = match repaint_after {
Expand Down Expand Up @@ -786,7 +829,7 @@ fn main() {
pt_gui.camera_offset += view_mat.transpose().transform_vector(&glm::vec3(x, y, 0.)).into();
}
}
if mouse_in_3d_viewport {
if mouse_in_3d_viewport && !pt_gui.import_window.open {
pt_gui.camera_scale *= 1.0 + (input.scroll_delta.y * -0.001)
}
}
Expand Down
Loading

0 comments on commit 77325a4

Please sign in to comment.