1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkOBJWriter.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkOBJWriter.h"
16 
17 #include "vtkDataSet.h"
18 #include "vtkErrorCode.h"
19 #include "vtkImageData.h"
20 #include "vtkInformation.h"
21 #include "vtkPNGWriter.h"
22 #include "vtkPointData.h"
23 #include "vtkPolyData.h"
24 #include "vtkSmartPointer.h"
25 #include "vtkTriangleStrip.h"
26 #include "vtksys/SystemTools.hxx"
27 
28 namespace
29 {
30 //----------------------------------------------------------------------------
WriteFaces(std::ofstream & f,vtkCellArray * faces,bool withNormals,bool withTCoords)31 void WriteFaces(std::ofstream& f, vtkCellArray* faces, bool withNormals, bool withTCoords)
32 {
33   vtkIdType npts;
34   vtkIdType* indx;
35   for (faces->InitTraversal(); faces->GetNextCell(npts, indx);)
36   {
37     f << "f";
38     for (vtkIdType i = 0; i < npts; i++)
39     {
40       f << " " << indx[i] + 1;
41       if (withTCoords)
42       {
43         f << "/" << indx[i] + 1;
44         if (withNormals)
45         {
46           f << "/" << indx[i] + 1;
47         }
48       }
49       else if (withNormals)
50       {
51         f << "//" << indx[i] + 1;
52       }
53     }
54     f << "\n";
55   }
56 }
57 
58 //----------------------------------------------------------------------------
WriteLines(std::ofstream & f,vtkCellArray * lines)59 void WriteLines(std::ofstream& f, vtkCellArray* lines)
60 {
61   vtkIdType npts;
62   vtkIdType* indx;
63   for (lines->InitTraversal(); lines->GetNextCell(npts, indx);)
64   {
65     f << "l";
66     for (vtkIdType i = 0; i < npts; i++)
67     {
68       f << " " << indx[i] + 1;
69     }
70     f << "\n";
71   }
72 }
73 
74 //----------------------------------------------------------------------------
WritePoints(std::ofstream & f,vtkPoints * pts,vtkDataArray * normals,vtkDataArray * tcoords)75 void WritePoints(std::ofstream& f, vtkPoints* pts, vtkDataArray* normals, vtkDataArray* tcoords)
76 {
77   vtkIdType nbPts = pts->GetNumberOfPoints();
78   // Positions
79   for (vtkIdType i = 0; i < nbPts; i++)
80   {
81     double p[3];
82     pts->GetPoint(i, p);
83     f << "v " << p[0] << " " << p[1] << " " << p[2] << "\n";
84   }
85 
86   // Normals
87   if (normals)
88   {
89     for (vtkIdType i = 0; i < nbPts; i++)
90     {
91       double p[3];
92       normals->GetTuple(i, p);
93       f << "vn " << p[0] << " " << p[1] << " " << p[2] << "\n";
94     }
95   }
96 
97   // Textures
98   if (tcoords)
99   {
100     for (vtkIdType i = 0; i < nbPts; i++)
101     {
102       double p[2];
103       tcoords->GetTuple(i, p);
104       f << "vt " << p[0] << " " << p[1] << "\n";
105     }
106   }
107 }
108 
109 //----------------------------------------------------------------------------
WriteTexture(std::ofstream & f,const std::string & baseName,vtkImageData * texture)110 bool WriteTexture(std::ofstream& f, const std::string& baseName, vtkImageData* texture)
111 {
112   std::string mtlName = baseName + ".mtl";
113   std::ofstream fmtl(mtlName, std::ofstream::out);
114   if (fmtl.fail())
115   {
116     return false;
117   }
118 
119   // write png file
120   std::string pngName = baseName + ".png";
121   vtkNew<vtkPNGWriter> pngWriter;
122   pngWriter->SetInputData(texture);
123   pngWriter->SetFileName(pngName.c_str());
124   pngWriter->Write();
125 
126   // remove directories
127   mtlName = vtksys::SystemTools::GetFilenameName(mtlName);
128   pngName = vtksys::SystemTools::GetFilenameName(pngName);
129 
130   // set material
131   fmtl << "newmtl vtktexture\n";
132   fmtl << "map_Kd " << pngName << "\n";
133 
134   // declare material in obj file
135   f << "mtllib " + mtlName + "\n";
136   f << "usemtl vtktexture\n";
137 
138   return true;
139 }
140 }
141 
142 //----------------------------------------------------------------------------
143 vtkStandardNewMacro(vtkOBJWriter);
144 
145 //----------------------------------------------------------------------------
vtkOBJWriter()146 vtkOBJWriter::vtkOBJWriter()
147 {
148   this->FileName = nullptr;
149   this->SetNumberOfInputPorts(2);
150 }
151 
152 //----------------------------------------------------------------------------
~vtkOBJWriter()153 vtkOBJWriter::~vtkOBJWriter()
154 {
155   this->SetFileName(nullptr);
156 }
157 
158 //----------------------------------------------------------------------------
WriteData()159 void vtkOBJWriter::WriteData()
160 {
161   vtkPolyData* input = this->GetInputGeometry();
162   vtkImageData* texture = this->GetInputTexture();
163 
164   if (input == nullptr)
165   {
166     vtkErrorMacro("No geometry to write!");
167     this->SetErrorCode(vtkErrorCode::UnknownError);
168     return;
169   }
170 
171   vtkPoints* pts = input->GetPoints();
172   vtkCellArray* polys = input->GetPolys();
173   vtkCellArray* strips = input->GetStrips();
174   vtkCellArray* lines = input->GetLines();
175   vtkDataArray* normals = input->GetPointData()->GetNormals();
176   vtkDataArray* tcoords = input->GetPointData()->GetTCoords();
177 
178   if (pts == nullptr)
179   {
180     vtkErrorMacro("No data to write!");
181     this->SetErrorCode(vtkErrorCode::UnknownError);
182     return;
183   }
184 
185   if (this->FileName == nullptr)
186   {
187     vtkErrorMacro("Please specify FileName to write");
188     this->SetErrorCode(vtkErrorCode::NoFileNameError);
189     return;
190   }
191 
192   vtkIdType npts = 0;
193 
194   std::ofstream f(this->FileName, std::ofstream::out);
195   if (f.fail())
196   {
197     vtkErrorMacro("Unable to open file: " << this->FileName);
198     this->SetErrorCode(vtkErrorCode::CannotOpenFileError);
199     return;
200   }
201 
202   // Write header
203   f << "# Generated by Visualization Toolkit\n";
204 
205   // Write material if a texture is specified
206   if (texture)
207   {
208     std::vector<std::string> comp;
209     vtksys::SystemTools::SplitPath(vtksys::SystemTools::GetFilenamePath(this->FileName), comp);
210     comp.push_back(vtksys::SystemTools::GetFilenameWithoutLastExtension(this->FileName));
211     if (!::WriteTexture(f, vtksys::SystemTools::JoinPath(comp), texture))
212     {
213       vtkErrorMacro("Unable to create material file");
214     }
215   }
216 
217   // Write points
218   ::WritePoints(f, pts, normals, tcoords);
219 
220   // Decompose any triangle strips into triangles
221   vtkNew<vtkCellArray> polyStrips;
222   if (strips->GetNumberOfCells() > 0)
223   {
224     vtkIdType* ptIds = nullptr;
225     for (strips->InitTraversal(); strips->GetNextCell(npts, ptIds);)
226     {
227       vtkTriangleStrip::DecomposeStrip(npts, ptIds, polyStrips);
228     }
229   }
230 
231   // Write triangle strips
232   ::WriteFaces(f, polyStrips, normals != nullptr, tcoords != nullptr);
233 
234   // Write polygons.
235   if (polys)
236   {
237     ::WriteFaces(f, polys, normals != nullptr, tcoords != nullptr);
238   }
239 
240   // Write lines.
241   if (lines)
242   {
243     ::WriteLines(f, lines);
244   }
245 
246   f.close();
247 }
248 
249 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)250 void vtkOBJWriter::PrintSelf(ostream& os, vtkIndent indent)
251 {
252   this->Superclass::PrintSelf(os, indent);
253 
254   os << indent << "FileName: " << (this->GetFileName() ? this->GetFileName() : "(none)") << endl;
255   os << indent << "Input: " << this->GetInputGeometry() << endl;
256 
257   vtkImageData* texture = this->GetInputTexture();
258   if (texture)
259   {
260     os << indent << "Texture:" << endl;
261     texture->PrintSelf(os, indent.GetNextIndent());
262   }
263 }
264 
265 //----------------------------------------------------------------------------
GetInputGeometry()266 vtkPolyData* vtkOBJWriter::GetInputGeometry()
267 {
268   return vtkPolyData::SafeDownCast(this->GetInput(0));
269 }
270 
271 //----------------------------------------------------------------------------
GetInputTexture()272 vtkImageData* vtkOBJWriter::GetInputTexture()
273 {
274   return vtkImageData::SafeDownCast(this->GetInput(1));
275 }
276 
277 //----------------------------------------------------------------------------
GetInput(int port)278 vtkDataSet* vtkOBJWriter::GetInput(int port)
279 {
280   return vtkDataSet::SafeDownCast(this->Superclass::GetInput(port));
281 }
282 
283 //----------------------------------------------------------------------------
FillInputPortInformation(int port,vtkInformation * info)284 int vtkOBJWriter::FillInputPortInformation(int port, vtkInformation* info)
285 {
286   if (port == 0)
287   {
288     info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkPolyData");
289     return 1;
290   }
291   if (port == 1)
292   {
293     info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData");
294     info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
295     return 1;
296   }
297   return 0;
298 }
299