Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper pof import #148

Merged
merged 7 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 25 additions & 11 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 All @@ -1410,7 +1420,7 @@ pub struct SubObject {
pub bsp_data: BspData,

// the following fields are derived information
pub(crate) children: Vec<ObjectId>,
pub children: Vec<ObjectId>,
pub is_debris_model: bool,

// "semantic name links", fields derived specifically from their names
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 @@ -2730,7 +2744,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
40 changes: 23 additions & 17 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use eframe::egui::{self, Button, TextStyle, Ui};
use pof::ObjectId;

use crate::{
ui_import::ImportWindow,
ui_properties_panel::{IndexingButtonsResponse, PropertiesPanel},
GlArrowhead, GlBufferedInsignia, GlBufferedShield, GlLollipops, GlObjectBuffers, Graphics, Model, POF_TOOLS_VERSION,
};
Expand Down Expand Up @@ -491,6 +492,7 @@ pub struct UiState {
pub properties_panel_dirty: bool,
pub last_selected_subobj: Option<ObjectId>,
pub properties_panel: PropertiesPanel,
pub import_window: ImportWindow,
pub display_radius: bool,
pub display_bbox: bool,
pub display_origin: bool,
Expand Down Expand Up @@ -907,25 +909,21 @@ impl PofToolsGui {
ui.close_menu();
}

if ui.button("Global Import").on_hover_text("Deletes data from the existing model and replaces it with the target model's. \n\
Replaces mass, moment of inertia, weapon points, docking points, thrusters, glow points, \
special points, turrets (only exact base and gun object name matches are retained), \
paths, eye points, and insignia.").clicked() {

let model = crossbeam::thread::scope(|s| s.spawn(|_| PofToolsGui::load_model(None)).join().unwrap()).unwrap();

if let Ok(Some(model)) = model {
self.model.global_import(Box::new(model.pof_model));
self.tree_view_selection = TreeValue::Header;
self.ui_state.refresh_properties_panel(&self.model);
self.viewport_3d_dirty = true;
}
if ui.button("Import").clicked() {
self.ui_state.import_window.open = !self.ui_state.import_window.open;
ui.close_menu();
}
});

ui.menu_button("View", |ui|{
if ui.button(if self.camera_orthographic {"Perspective"} else {"Orthographic"}).clicked() {
if self.ui_state.show_import_window(&self.model, ctx) {
self.merge_import_model();
self.import_window.open = false;
self.finish_loading_model(display);
self.model.recalc_semantic_name_links();
}

ui.menu_button("View", |ui| {
if ui.button(if self.camera_orthographic { "Perspective" } else { "Orthographic" }).clicked() {
self.camera_orthographic = !self.camera_orthographic;
ui.close_menu();
}
Expand Down Expand Up @@ -985,12 +983,20 @@ impl PofToolsGui {

ui.separator();

if ui.add_enabled(undo_history.can_undo(), egui::Button::new("⎗")).on_hover_text("Undo").clicked() {
if ui
.add_enabled(undo_history.can_undo(), egui::Button::new("⎗"))
.on_hover_text("Undo")
.clicked()
{
undo_history.undo(&mut *self.model);
self.sanitize_ui_state();
}

if ui.add_enabled(undo_history.can_redo(), egui::Button::new("⎘")).on_hover_text("Redo").clicked() {
if ui
.add_enabled(undo_history.can_redo(), egui::Button::new("⎘"))
.on_hover_text("Redo")
.clicked()
{
undo_history.redo(&mut *self.model);
self.sanitize_ui_state();
}
Expand Down
Loading
Loading