diff --git a/plugins/model/Makefile.am b/plugins/model/Makefile.am index d0d6ea6363..bf46eeb12d 100644 --- a/plugins/model/Makefile.am +++ b/plugins/model/Makefile.am @@ -11,6 +11,7 @@ model_la_LIBADD = $(top_builddir)/libs/picomodel/libpicomodel.la \ model_la_SOURCES = AseExporter.cpp \ Lwo2Chunk.cpp \ Lwo2Exporter.cpp \ + WavefrontExporter.cpp \ PicoModelNode.cpp \ RenderablePicoModel.cpp \ PicoModelLoader.cpp \ diff --git a/plugins/model/PicoModelModule.h b/plugins/model/PicoModelModule.h index f8e068f533..0dffe65df3 100644 --- a/plugins/model/PicoModelModule.h +++ b/plugins/model/PicoModelModule.h @@ -12,6 +12,7 @@ #include "PicoModelLoader.h" #include "AseExporter.h" #include "Lwo2Exporter.h" +#include "WavefrontExporter.h" typedef unsigned char byte; @@ -113,6 +114,7 @@ class PicoModelModule : GlobalModelFormatManager().registerExporter(std::make_shared()); GlobalModelFormatManager().registerExporter(std::make_shared()); + GlobalModelFormatManager().registerExporter(std::make_shared()); } }; diff --git a/plugins/model/WavefrontExporter.cpp b/plugins/model/WavefrontExporter.cpp new file mode 100644 index 0000000000..74117704c4 --- /dev/null +++ b/plugins/model/WavefrontExporter.cpp @@ -0,0 +1,88 @@ +#include "WavefrontExporter.h" + +#include "itextstream.h" +#include "imodelsurface.h" +#include "imap.h" + +#include +#include + +namespace model +{ + +WavefrontExporter::WavefrontExporter() +{} + +IModelExporterPtr WavefrontExporter::clone() +{ + return std::make_shared(); +} + +const std::string& WavefrontExporter::getDisplayName() const +{ + static std::string _extension("Wavefront OBJ"); + return _extension; +} + +const std::string& WavefrontExporter::getExtension() const +{ + static std::string _extension("OBJ"); + return _extension; +} + +void WavefrontExporter::exportToStream(std::ostream& stream) +{ + // Count exported vertices. Exported indices are 1-based though. + std::size_t vertexCount = 0; + + // Each surface is exported as group. + for (const Surfaces::value_type& pair : _surfaces) + { + const Surface& surface = pair.second; + + // Base index for vertices, added to the surface indices + std::size_t vertBaseIndex = vertexCount; + + // Since we don't write .mtl files store at least the material into the group name + stream << "g " << surface.materialName << std::endl; + stream << std::endl; + + // Temporary buffers for vertices, texcoords and polys + std::stringstream vertexBuf; + std::stringstream texCoordBuf; + std::stringstream polyBuf; + + for (const ArbitraryMeshVertex& meshVertex : surface.vertices) + { + // Write coordinates into the export buffers + const Vector3& vert = meshVertex.vertex; + const Vector2& uv = meshVertex.texcoord; + + vertexBuf << "v " << vert.x() << " " << vert.y() << " " << vert.z() << "\n"; + texCoordBuf << "vt " << uv.x() << " " << uv.y() << "\n"; + + vertexCount++; + } + + // Every three indices form a triangle. Indices are 1-based so add +1 to each index + for (std::size_t i = 0; i + 2 < surface.indices.size(); i += 3) + { + std::size_t index1 = vertBaseIndex + static_cast(surface.indices[i+0]) + 1; + std::size_t index2 = vertBaseIndex + static_cast(surface.indices[i+1]) + 1; + std::size_t index3 = vertBaseIndex + static_cast(surface.indices[i+2]) + 1; + + // f 1/1 3/3 2/2 + polyBuf << "f"; + polyBuf << " " << index1 << "/" << index1; + polyBuf << " " << index2 << "/" << index2; + polyBuf << " " << index3 << "/" << index3; + polyBuf << "\n"; + } + + stream << vertexBuf.str() << std::endl; + stream << texCoordBuf.str() << std::endl; + stream << polyBuf.str() << std::endl; + } +} + +} diff --git a/plugins/model/WavefrontExporter.h b/plugins/model/WavefrontExporter.h new file mode 100644 index 0000000000..5813b0102b --- /dev/null +++ b/plugins/model/WavefrontExporter.h @@ -0,0 +1,31 @@ +#pragma once + +#include "imodel.h" +#include "ModelExporterBase.h" + +namespace model +{ + +class WavefrontExporter : + public ModelExporterBase +{ +public: + WavefrontExporter(); + + IModelExporterPtr clone() override; + + Format getFileFormat() const override + { + return Format::Text; + } + + const std::string& getDisplayName() const override; + + // Returns the uppercase file extension this exporter is suitable for + const std::string& getExtension() const override; + + // Export the model file to the given stream + void exportToStream(std::ostream& stream) override; +}; + +} diff --git a/tools/msvc/model.vcxproj b/tools/msvc/model.vcxproj index 04526e4c13..c92988dadc 100644 --- a/tools/msvc/model.vcxproj +++ b/tools/msvc/model.vcxproj @@ -323,6 +323,7 @@ + @@ -335,6 +336,7 @@ + diff --git a/tools/msvc/model.vcxproj.filters b/tools/msvc/model.vcxproj.filters index 5e1033b689..3bbbb9e530 100644 --- a/tools/msvc/model.vcxproj.filters +++ b/tools/msvc/model.vcxproj.filters @@ -31,6 +31,9 @@ src + + src + @@ -63,5 +66,8 @@ src + + src + \ No newline at end of file