MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ModelWriter.hpp
Go to the documentation of this file.
1#pragma once
2
4
5namespace MayaFlux::IO {
6
7/**
8 * @struct ModelWriteOptions
9 * @brief Configuration for model writing.
10 *
11 * Format-specific knobs are interpreted by the concrete writer; unsupported
12 * options are silently ignored.
13 */
15 /**
16 * @brief Assimp export format id, e.g. "gltf2", "objnomtl", "collada",
17 * "stl", "fbx", "fbxa".
18 *
19 * File extension alone is ambiguous for several formats Assimp exports:
20 * OBJ with or without a sidecar .mtl, FBX binary vs. ASCII both use
21 * ".fbx". Empty (the default) derives a format id from the extension
22 * via a small, deliberately short allow-list; see AssimpModelWriter's
23 * own doc for exactly which extensions resolve on their own and which
24 * require this to be set explicitly.
25 */
26 std::string format_id;
27
28 /**
29 * @brief Name to embed for the exported material, if the target format
30 * carries one. Empty uses the source submesh's material_name.
31 */
33};
34
35/**
36 * @class ModelWriter
37 * @brief Abstract base for 3D model format writers.
38 *
39 * Parallels ImageWriter/VolumeWriter. A writer accepts one or more
40 * Kakshya::MeshData (one per aiMesh in the exported scene, mirroring how
41 * ModelReader produces one MeshData per aiMesh on import) and is
42 * responsible for validating that the target format can express what it's
43 * given: a format with no non-triangle primitive support rejects point/line
44 * data rather than silently dropping it. See AssimpModelWriter's own doc
45 * for the concrete list of formats verified safe for which topology.
46 *
47 * Writers are single-shot: one call to write() produces one file. MeshData
48 * is always CPU-authoritative in this codebase (see MeshBuffer's mutation
49 * path), so no GPU download step belongs here. A caller with GPU-only
50 * mesh state (ComputeMeshBuffer) downloads to MeshData first via
51 * IO::download_compute_mesh, then calls write() like any other source.
52 */
53class MAYAFLUX_API ModelWriter {
54public:
55 virtual ~ModelWriter() = default;
56
57 /**
58 * @brief Check whether this writer handles the given filepath.
59 */
60 [[nodiscard]] virtual bool can_write(const std::string& filepath) const = 0;
61
62 /**
63 * @brief Write one or more meshes to disk as a single scene.
64 * @param filepath Destination path.
65 * @param meshes One or more meshes. Must each satisfy MeshData::is_valid().
66 * @param options Format-specific options.
67 * @return true on success. On failure call get_last_error().
68 */
69 virtual bool write(
70 const std::string& filepath,
71 const std::vector<Kakshya::MeshData>& meshes,
72 const ModelWriteOptions& options = {})
73 = 0;
74
75 /**
76 * @brief File extensions handled by this writer (without dot).
77 */
78 [[nodiscard]] virtual std::vector<std::string> get_supported_extensions() const = 0;
79
80 /**
81 * @brief Last error message or empty string.
82 */
83 [[nodiscard]] virtual std::string get_last_error() const = 0;
84};
85
86using ModelWriterFactory = std::function<std::unique_ptr<ModelWriter>()>;
87
88/**
89 * @class ModelWriterRegistry
90 * @brief Singleton registry dispatching model writes by file extension.
91 *
92 * Mirrors ImageWriterRegistry/VolumeWriterRegistry. Concrete writers
93 * register themselves during subsystem init. create_writer(path) looks up
94 * the extension and returns a fresh instance, or nullptr if none is
95 * registered.
96 */
97class MAYAFLUX_API ModelWriterRegistry {
98public:
100 {
101 static ModelWriterRegistry registry;
102 return registry;
103 }
104
106 const std::vector<std::string>& extensions,
107 const ModelWriterFactory& factory)
108 {
109 for (const auto& ext : extensions) {
110 m_factories[ext] = factory;
111 }
112 }
113
114 [[nodiscard]] std::unique_ptr<ModelWriter> create_writer(const std::string& filepath) const
115 {
116 auto ext = std::filesystem::path(filepath).extension().string();
117 if (!ext.empty() && ext[0] == '.') {
118 ext = ext.substr(1);
119 }
120
121 auto it = m_factories.find(ext);
122 if (it != m_factories.end()) {
123 return it->second();
124 }
125 return nullptr;
126 }
127
128 [[nodiscard]] std::vector<std::string> get_registered_extensions() const
129 {
130 std::vector<std::string> exts;
131 exts.reserve(m_factories.size());
132 for (const auto& [ext, _] : m_factories) {
133 exts.push_back(ext);
134 }
135 return exts;
136 }
137
138private:
139 std::unordered_map<std::string, ModelWriterFactory> m_factories;
140};
141
142} // namespace MayaFlux::IO
std::unordered_map< std::string, ModelWriterFactory > m_factories
std::unique_ptr< ModelWriter > create_writer(const std::string &filepath) const
void register_writer(const std::vector< std::string > &extensions, const ModelWriterFactory &factory)
std::vector< std::string > get_registered_extensions() const
static ModelWriterRegistry & instance()
Singleton registry dispatching model writes by file extension.
virtual ~ModelWriter()=default
virtual std::vector< std::string > get_supported_extensions() const =0
File extensions handled by this writer (without dot).
virtual bool can_write(const std::string &filepath) const =0
Check whether this writer handles the given filepath.
virtual std::string get_last_error() const =0
Last error message or empty string.
virtual bool write(const std::string &filepath, const std::vector< Kakshya::MeshData > &meshes, const ModelWriteOptions &options={})=0
Write one or more meshes to disk as a single scene.
Abstract base for 3D model format writers.
std::function< std::unique_ptr< ModelWriter >()> ModelWriterFactory
std::string material_name_override
Name to embed for the exported material, if the target format carries one.
std::string format_id
Assimp export format id, e.g.
Configuration for model writing.