23#ifndef VCL_IO_MESH_OBJ_SAVE_H
24#define VCL_IO_MESH_OBJ_SAVE_H
28#include <vclib/io/file_info.h>
29#include <vclib/io/image/save.h>
30#include <vclib/io/mesh/settings.h>
31#include <vclib/io/write.h>
33#include <vclib/space/complex.h>
34#include <vclib/space/core.h>
42template<VertexConcept VertexType, MeshConcept MeshType>
43ObjMaterial objMaterialFromVertex(
49 if constexpr (HasPerVertexColor<MeshType>) {
50 if (fi.hasPerVertexColor()) {
52 mat.Kd.x() = v.color().redF();
53 mat.Kd.y() = v.color().greenF();
54 mat.Kd.z() = v.color().blueF();
57 if constexpr (HasPerVertexTexCoord<MeshType>) {
58 if (fi.hasPerVertexTexCoord()) {
59 mat.hasTexture =
true;
60 if constexpr (HasTexturePaths<MeshType>) {
61 mat.map_Kd = m.texturePath(v.texCoord().index());
68template<FaceConcept FaceType, MeshConcept MeshType>
69ObjMaterial objMaterialFromFace(
75 if constexpr (HasPerFaceColor<MeshType>) {
76 if (fi.hasPerFaceColor()) {
78 mat.Kd.x() = f.color().redF();
79 mat.Kd.y() = f.color().greenF();
80 mat.Kd.z() = f.color().blueF();
83 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
84 if (fi.hasPerFaceWedgeTexCoords()) {
85 mat.hasTexture =
true;
86 if constexpr (HasTexturePaths<MeshType>) {
87 mat.map_Kd = m.texturePath(f.textureIndex());
94template<EdgeConcept EdgeType, MeshConcept MeshType>
95ObjMaterial objMaterialFromEdge(
const EdgeType& e,
const MeshInfo& fi)
98 if constexpr (HasPerEdgeColor<MeshType>) {
99 if (fi.hasPerEdgeColor()) {
101 mat.Kd.x() = e.color().redF();
102 mat.Kd.y() = e.color().greenF();
103 mat.Kd.z() = e.color().blueF();
110 ElementConcept ElementType,
111 MeshConcept MeshType,
112 LoggerConcept LogType = NullLogger>
113void writeElementObjMaterial(
114 const ElementType& e,
117 ObjMaterial& lastMaterial,
118 std::map<ObjMaterial, std::string>& materialMap,
121 const SaveSettings& settings,
125 constexpr bool EL_IS_VERTEX = ElementType::ELEMENT_ID == ElemId::VERTEX;
126 constexpr bool EL_IS_FACE = ElementType::ELEMENT_ID == ElemId::FACE;
127 constexpr bool EL_IS_EDGE = ElementType::ELEMENT_ID == ElemId::EDGE;
129 if constexpr (EL_IS_VERTEX) {
130 mat = objMaterialFromVertex<typename MeshType::VertexType, MeshType>(
133 if constexpr (EL_IS_FACE) {
134 mat = objMaterialFromFace(e, m, fi);
136 if constexpr (EL_IS_EDGE) {
137 mat = objMaterialFromEdge<typename MeshType::EdgeType, MeshType>(e, fi);
139 if (!mat.isEmpty()) {
140 static const std::string MATERIAL_PREFIX =
"MATERIAL_";
142 auto it = materialMap.find(mat);
143 if (it == materialMap.end()) {
145 mname = MATERIAL_PREFIX + std::to_string(materialMap.size());
146 materialMap[mat] = mname;
148 mtlfp <<
"newmtl " << mname << std::endl;
149 mtlfp << mat << std::endl;
150 if constexpr (HasTextureImages<MeshType>) {
151 if (settings.saveTextureImages && mat.hasTexture) {
155 uint textureIndex = 0;
156 if constexpr (EL_IS_FACE) {
157 textureIndex = e.textureIndex();
159 const Texture& t = m.texture(textureIndex);
161 saveImage(t.image(), m.meshBasePath() + mat.map_Kd);
163 catch (
const std::runtime_error& e) {
164 log.log(e.what(), LogType::WARNING_LOG);
174 if (mat != lastMaterial) {
176 fp <<
"usemtl " << mname << std::endl;
181template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
184 const std::string& filename,
188 const SaveSettings& settings = SaveSettings(),
191 MeshInfo meshInfo(m);
197 if (!settings.info.isEmpty())
198 meshInfo = settings.info.intersect(meshInfo);
203 if (meshInfo.hasPerVertexTexCoord() &&
204 meshInfo.hasPerFaceWedgeTexCoords()) {
205 meshInfo.setPerVertexTexCoord(
false);
208 std::ofstream mtlftmp;
209 std::map<detail::ObjMaterial, std::string> materialMap;
212 meshInfo.hasPerVertexColor() || meshInfo.hasPerFaceColor() ||
213 (meshInfo.hasTextures() && (meshInfo.hasPerVertexTexCoord() ||
214 meshInfo.hasPerFaceWedgeTexCoords()));
217 std::string mtlFileName =
220 mtlftmp = openOutputFileStream(
224 fp <<
"mtllib ./" << mtlFileName << std::endl;
226 else if (mtlfp ==
nullptr) {
231 detail::ObjMaterial lastMaterial;
234 using VertexType = MeshType::VertexType;
236 fp << std::endl <<
"# Vertices" << std::endl;
238 for (
const VertexType& v : m.
vertices()) {
240 detail::writeElementObjMaterial<VertexType, MeshType>(
252 io::writeDouble(fp, v.position().x(),
false);
253 io::writeDouble(fp, v.position().y(),
false);
254 io::writeDouble(fp, v.position().z(),
false);
257 if constexpr (HasPerVertexNormal<MeshType>) {
258 if (meshInfo.hasPerVertexNormal()) {
260 io::writeDouble(fp, v.normal().x(),
false);
261 io::writeDouble(fp, v.normal().y(),
false);
262 io::writeDouble(fp, v.normal().z(),
false);
266 if constexpr (HasPerVertexTexCoord<MeshType>) {
267 if (meshInfo.hasPerVertexTexCoord()) {
269 io::writeFloat(fp, v.texCoord().u(),
false);
270 io::writeFloat(fp, v.texCoord().v(),
false);
277 if constexpr (HasFaces<MeshType>) {
278 using VertexType = MeshType::VertexType;
279 using FaceType = MeshType::FaceType;
281 if (meshInfo.hasFaces()) {
282 fp << std::endl <<
"# Faces" << std::endl;
285 std::vector<uint> vIndices = m.vertexCompactIndices();
287 uint wedgeTexCoord = 1;
288 for (
const FaceType& f : m.
faces()) {
290 detail::writeElementObjMaterial(
301 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
302 if (meshInfo.hasPerFaceWedgeTexCoords()) {
303 using WedgeTexCoordType = FaceType::WedgeTexCoordType;
304 for (
const WedgeTexCoordType wt : f.wedgeTexCoords()) {
306 io::writeFloat(fp, wt.u(),
false);
307 io::writeFloat(fp, wt.v(),
false);
314 for (
const VertexType* v : f.
vertices()) {
315 fp << vIndices[m.index(v)] + 1;
316 if constexpr (HasPerVertexTexCoord<MeshType>) {
319 if (meshInfo.hasPerVertexTexCoord()) {
320 fp <<
"/" << vIndices[m.index(v)] + 1;
323 if constexpr (HasPerFaceWedgeTexCoords<MeshType>) {
327 if (meshInfo.hasPerFaceWedgeTexCoords()) {
328 fp <<
"/" << wedgeTexCoord++;
338 if constexpr (HasEdges<MeshType>) {
339 using VertexType = MeshType::VertexType;
340 using EdgeType = MeshType::EdgeType;
342 if (meshInfo.hasEdges()) {
343 fp << std::endl <<
"# Edges" << std::endl;
346 std::vector<uint> vIndices = m.vertexCompactIndices();
348 for (
const EdgeType& e : m.
edges()) {
350 detail::writeElementObjMaterial(
362 fp << vIndices[m.index(e.vertex(0))] + 1 <<
" ";
363 fp << vIndices[m.index(e.vertex(1))] + 1 << std::endl;
371template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
376 const SaveSettings& settings,
379 detail::saveObj(m,
"materials", fp, &mtlfp,
false, settings, log);
382template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
386 const SaveSettings& settings,
389 detail::saveObj(m,
"", fp,
nullptr,
false, settings, log);
392template<MeshConcept MeshType, LoggerConcept LogType = NullLogger>
395 const std::string& filename,
396 const SaveSettings& settings,
399 std::ofstream fp = openOutputFileStream(filename,
"obj");
401 detail::saveObj(m, filename, fp,
nullptr,
true, settings, log);
static std::string fileNameWithExtension(const std::string &fullpath)
Get the filename with extension of a file.
Definition file_info.h:240
static std::string pathWithoutFileName(const std::string &fullpath)
Get the path of a file.
Definition file_info.h:200
NullLogger nullLogger
The nullLogger object is an object of type NullLogger that is used as default argument in the functio...
Definition null_logger.h:123
constexpr detail::FacesView faces
A view that allows to iterate overt the Face elements of an object.
Definition face.h:84
constexpr detail::EdgesView edges
A view that allows to iterate overt the Edge elements of an object.
Definition edge.h:84
constexpr detail::VerticesView vertices
A view that allows to iterate over the Vertex elements of an object.
Definition vertex.h:92