feat(io): FBXEXPORT writes the drawing's 3D meshes as binary FBX

FBXEXPORT (also EXPORTFBX) saves every 3D mesh of the drawing (solids,
surfaces and meshes, at full display resolution) as a binary FBX 7.4
file for visualisation tools: one model per object with its geometry,
face normals and a material in its display colour. Axes stay Z-up and
the unit scale follows the drawing's insertion units.

Refs #1502

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-13 16:37:09 +03:00
1 parent 12ffc3961f
commit f18386c100
30 files changed
+678

No files matched your search

+56
View File
@@ -1994,6 +1994,62 @@ impl OpenCADStudio {
}
}
// ── FBX export ────────────────────────────────────────────────
Message::FbxExport => {
let i = self.active_tab;
if self.tabs[i].scene.meshes.is_empty() {
self.command_line.push_error(
crate::t!("FBXEXPORT: no 3D mesh data in this drawing.").as_ref(),
);
return Task::none();
}
#[cfg(not(target_arch = "wasm32"))]
{
Task::perform(
async {
crate::sys::file_dialog()
.set_title(crate::t!("Export FBX").as_ref())
.set_file_name("export.fbx")
.add_filter(crate::t!("FBX Files").as_ref(), &["fbx"])
.add_filter(crate::t!("All Files").as_ref(), &["*"])
.save_file()
.await
.map(|h| crate::sys::handle_path(&h))
},
Message::FbxExportPath,
)
}
#[cfg(target_arch = "wasm32")]
{
let stem = self.tabs[i]
.current_path
.as_deref()
.and_then(|p: &std::path::Path| p.file_stem())
.map(|s: &std::ffi::OsStr| s.to_string_lossy().into_owned())
.unwrap_or_else(|| "drawing".into());
Task::perform(
crate::io::fbx::pick_fbx_path_owned(stem),
Message::FbxExportPath,
)
}
}
Message::FbxExportPath(Some(path)) => self.on_fbx_export_path_some(path),
Message::FbxExportPath(None) => Task::none(),
Message::FbxExportFinished(path, result) => {
match result {
Ok(()) => self.command_line.push_output(
crate::tf!("FBXEXPORT: exported to \"{}\"", path.display()).as_ref(),
),
Err(error) => self
.command_line
.push_error(crate::tf!("FBXEXPORT: {error}").as_ref()),
}
Task::none()
}
Message::StepExportPath(Some(path)) => self.on_step_export_path_some(path),
Message::StepExportPath(None) => Task::none(),