MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ write()

bool MayaFlux::IO::AssimpModelWriter::write ( const std::string &  filepath,
const std::vector< Kakshya::MeshData > &  meshes,
const ModelWriteOptions options = {} 
)
overridevirtual

Write one or more meshes to disk as a single scene.

Parameters
filepathDestination path.
meshesOne or more meshes. Must each satisfy MeshData::is_valid().
optionsFormat-specific options.
Returns
true on success. On failure call get_last_error().

Implements MayaFlux::IO::ModelWriter.

Definition at line 111 of file AssimpModelWriter.cpp.

115{
116 m_last_error.clear();
117
118 if (meshes.empty()) {
119 set_error("No meshes to write");
120 return false;
121 }
122
123 for (const auto& m : meshes) {
124 if (!m.is_valid()) {
125 set_error("MeshData failed is_valid()");
126 return false;
127 }
128 if (m.layout.stride_bytes != sizeof(Kakshya::MeshVertex)) {
129 set_error("MeshData vertex stride is not the canonical MeshVertex layout");
130 return false;
131 }
132 }
133
134 const auto ext = extension_of(filepath);
135 std::string format_id = options.format_id;
136 if (format_id.empty()) {
137 format_id = default_format_id(ext);
138 }
139 if (format_id.empty()) {
140 set_error("No format_id resolved for extension '" + ext
141 + "', pass ModelWriteOptions::format_id explicitly");
142 return false;
143 }
144
145 // NOLINTBEGIN(cppcoreguidelines-owning-memory)
146 // aiScene is a C-style struct: every array is a raw owning pointer freed
147 // by aiScene's own destructor (and aiMesh's/aiMaterial's for their own
148 // sub-arrays). A smart pointer cannot be substituted at this boundary.
149 aiScene scene;
150 scene.mRootNode = new aiNode();
151
152 scene.mNumMaterials = static_cast<unsigned int>(meshes.size());
153 scene.mMaterials = new aiMaterial*[scene.mNumMaterials];
154
155 scene.mNumMeshes = static_cast<unsigned int>(meshes.size());
156 scene.mMeshes = new aiMesh*[scene.mNumMeshes];
157
158 scene.mRootNode->mNumMeshes = scene.mNumMeshes;
159 scene.mRootNode->mMeshes = new unsigned int[scene.mNumMeshes];
160
161 for (unsigned int mi = 0; mi < scene.mNumMeshes; ++mi) {
162 const auto& mesh_data = meshes[mi];
163 const auto info = submesh_info_for(mesh_data);
164
165 auto* mat = new aiMaterial();
166 const std::string mat_name = !options.material_name_override.empty()
167 ? options.material_name_override
168 : (!info.material_name.empty() ? info.material_name : "material_" + std::to_string(mi));
169 aiString ai_mat_name(mat_name);
170 mat->AddProperty(&ai_mat_name, AI_MATKEY_NAME);
171 if (!info.diffuse_path.empty()) {
172 aiString tex_path(info.diffuse_path);
173 mat->AddProperty(&tex_path, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
174 }
175 scene.mMaterials[mi] = mat;
176
177 auto* mesh = new aiMesh();
178 mesh->mMaterialIndex = mi;
179 mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
180 mesh->mName = aiString(!info.name.empty() ? info.name : "mesh_" + std::to_string(mi));
181
182 const auto* vb = std::get_if<std::vector<uint8_t>>(&mesh_data.vertex_variant);
183 const auto* ib = std::get_if<std::vector<uint32_t>>(&mesh_data.index_variant);
184
185 const auto* verts = reinterpret_cast<const Kakshya::MeshVertex*>(vb->data());
186 const size_t vertex_count = vb->size() / sizeof(Kakshya::MeshVertex);
187
188 mesh->mNumVertices = static_cast<unsigned int>(vertex_count);
189 mesh->mVertices = new aiVector3D[vertex_count];
190 mesh->mNormals = new aiVector3D[vertex_count];
191 mesh->mTangents = new aiVector3D[vertex_count];
192 mesh->mTextureCoords[0] = new aiVector3D[vertex_count];
193 mesh->mNumUVComponents[0] = 2;
194 mesh->mColors[0] = new aiColor4D[vertex_count];
195
196 for (size_t v = 0; v < vertex_count; ++v) {
197 const auto& mv = verts[v];
198 mesh->mVertices[v] = aiVector3D(mv.position.x, mv.position.y, mv.position.z);
199 mesh->mNormals[v] = aiVector3D(mv.normal.x, mv.normal.y, mv.normal.z);
200 mesh->mTangents[v] = aiVector3D(mv.tangent.x, mv.tangent.y, mv.tangent.z);
201 mesh->mTextureCoords[0][v] = aiVector3D(mv.uv.x, mv.uv.y, 0.0F);
202 mesh->mColors[0][v] = aiColor4D(mv.color.x, mv.color.y, mv.color.z, 1.0F);
203 }
204
205 const size_t face_count = ib->size() / 3;
206 mesh->mNumFaces = static_cast<unsigned int>(face_count);
207 mesh->mFaces = new aiFace[face_count];
208 for (size_t f = 0; f < face_count; ++f) {
209 auto& face = mesh->mFaces[f];
210 face.mNumIndices = 3;
211 face.mIndices = new unsigned int[3] {
212 (*ib)[(f * 3) + 0],
213 (*ib)[(f * 3) + 1],
214 (*ib)[(f * 3) + 2],
215 };
216 }
217
218 scene.mMeshes[mi] = mesh;
219 scene.mRootNode->mMeshes[mi] = mi;
220 }
221 // NOLINTEND(cppcoreguidelines-owning-memory)
222
223 const auto resolved = resolve_write_path(filepath);
224
225 Assimp::Exporter exporter;
226 const auto ret = exporter.Export(&scene, format_id, resolved);
227
228 if (ret != AI_SUCCESS) {
229 set_error(exporter.GetErrorString());
231 "AssimpModelWriter: export failed for '{}' (format '{}'): {}",
232 resolved, format_id, m_last_error);
233 return false;
234 }
235
237 "AssimpModelWriter: wrote '{}', {} mesh(es), format '{}'",
238 resolved, meshes.size(), format_id);
239 return true;
240}
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
void set_error(std::string msg) const
std::string_view to_string(Keys key) noexcept
Converts a Keys enum value to its string representation.
Definition Keys.cpp:55
std::string resolve_write_path(const std::string &filepath)
Anchor a relative output path to Config::SOURCE_DIR.
@ FileIO
Filesystem I/O operations.
@ IO
Networking, file handling, streaming.

References MayaFlux::Journal::FileIO, MayaFlux::IO::ModelWriteOptions::format_id, MayaFlux::Journal::IO, m_last_error, MayaFlux::IO::ModelWriteOptions::material_name_override, MF_ERROR, MF_INFO, MayaFlux::IO::resolve_write_path(), and set_error().

+ Here is the call graph for this function: