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