objToVTK.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2015 OpenFOAM Foundation
9  Copyright (C) 2019-2025 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 Application
28  objToVTK
29 
30 Group
31  grpMeshManipulationUtilities
32 
33 Description
34  Read obj line (not surface) file and convert into legacy VTK file.
35 
36 \*---------------------------------------------------------------------------*/
37 
38 #include "argList.H"
39 #include "OFstream.H"
40 #include "stringOps.H"
41 #include "point.H"
42 #include "DynamicList.H"
43 
44 using namespace Foam;
45 
46 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
47 
48 string getLine(std::ifstream& is)
49 {
50  string line;
51  do
52  {
53  std::getline(is, line);
54  }
55  while (line.starts_with('#'));
56 
57  return line;
58 }
59 
60 
61 // Token list with one of the following:
62 // f v1 v2 v3 ...
63 // f v1/vt1 v2/vt2 v3/vt3 ...
64 // l v1 v2 v3 ...
65 // l v1/vt1 v2/vt2 v3/vt3 ...
66 static label readObjVertices
67 (
68  const SubStrings& tokens,
69  DynamicList<label>& verts
70 )
71 {
72  verts.clear();
73 
74  bool first = true;
75  for (const auto& tok : tokens)
76  {
77  if (first)
78  {
79  // skip initial "f" or "l"
80  first = false;
81  continue;
82  }
83 
84  std::string vrtSpec(tok.str());
85 
86  if
87  (
88  const auto slash = vrtSpec.find('/');
89  slash != std::string::npos
90  )
91  {
92  vrtSpec.erase(slash);
93  }
94 
95  label vertId = readLabel(vrtSpec);
96 
97  verts.push_back(vertId - 1);
98  }
99 
100  return verts.size();
101 }
102 
103 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
104 
105 int main(int argc, char *argv[])
106 {
108  (
109  "Read obj line (not surface) file and convert into legacy VTK file"
110  );
111 
113  argList::addArgument("obj-file", "The input obj line file");
114  argList::addArgument("vtk-file", "The output vtk file");
115  argList args(argc, argv);
116 
117  const auto objName = args.get<fileName>(1);
118  const auto outName = args.get<fileName>(2);
119 
120  std::ifstream OBJfile(objName);
121 
122  Info<< "Processing file " << objName << endl;
123 
124  if (!OBJfile.good())
125  {
127  << "Cannot read file " << objName << exit(FatalError);
128  }
129 
130  // Points and lines
132  DynamicList<vector> pointNormals;
133  DynamicList<labelList> polyLines;
134  DynamicList<labelList> polygons;
135 
136  DynamicList<label> dynVerts;
137 
138  bool hasWarned = false;
139 
140  label lineNo = 0;
141  while (OBJfile.good())
142  {
143  const string line = getLine(OBJfile);
144  lineNo++;
145 
146  if (line.empty()) continue;
147 
148  const auto tokens = stringOps::splitSpace(line);
149 
150  // Require command and some arguments
151  if (tokens.size() < 2)
152  {
153  continue;
154  }
155 
156  const word cmd = word::validate(tokens[0]);
157 
158  if (cmd == "v")
159  {
160  // Vertex
161  // v x y z
162 
164  (
165  readScalar(tokens[1]),
166  readScalar(tokens[2]),
167  readScalar(tokens[3])
168  );
169  }
170  else if (cmd == "vn")
171  {
172  // Vertex normals
173  // vn x y z
174 
175  pointNormals.emplace_back
176  (
177  readScalar(tokens[1]),
178  readScalar(tokens[2]),
179  readScalar(tokens[3])
180  );
181  }
182  else if (cmd == "l")
183  {
184  // Line
185  // l v1 v2 v3 ...
186  // OR
187  // l v1/vt1 v2/vt2 v3/vt3 ...
188 
189  readObjVertices(tokens, dynVerts);
190  polyLines.emplace_back() = dynVerts;
191  }
192  else if (cmd == "f")
193  {
194  // f v1 v2 v3 ...
195  // OR
196  // f v1/vt1 v2/vt2 v3/vt3 ...
197 
198  readObjVertices(tokens, dynVerts);
199  polygons.emplace_back() = dynVerts;
200  }
201  else if (cmd != "")
202  {
203  if (!hasWarned)
204  {
205  hasWarned = true;
206 
208  << "Unrecognized OBJ command " << cmd << nl
209  << "In line " << line
210  << " at linenumber " << lineNo << nl
211  << "Only recognized commands are 'v' and 'l'.\n"
212  << "If this is a surface command use surfaceConvert instead"
213  << " to convert to a file format that can be read by VTK"
214  << endl;
215  }
216  }
217  }
218 
219 
220  //
221  // Write as vtk 'polydata' file
222  //
223 
224 
225  OFstream outFile(outName);
226 
227  outFile
228  << "# vtk DataFile Version 2.0\n"
229  << objName << nl
230  << "ASCII\n"
231  << "DATASET POLYDATA\n"
232  << "POINTS " << points.size() << " double\n";
233 
234  for (const point& pt : points)
235  {
236  outFile
237  << float(pt.x()) << ' '
238  << float(pt.y()) << ' '
239  << float(pt.z()) << nl;
240  }
241 
242  outFile
243  << "VERTICES " << points.size() << ' ' << (2 * points.size()) << nl;
244 
245  forAll(points, i)
246  {
247  outFile << 1 << ' ' << i << nl;
248  }
249 
250  label nItems = 0;
251  for (const labelList& line : polyLines)
252  {
253  nItems += line.size() + 1;
254  }
255 
256  outFile
257  << "LINES " << polyLines.size() << ' ' << nItems << nl;
258 
259  for (const labelList& line : polyLines)
260  {
261  outFile << line.size();
262 
263  for (const label vrt : line)
264  {
265  outFile << ' ' << vrt;
266  }
267  outFile << nl;
268  }
269 
270 
271  nItems = 0;
272  for (const labelList& line : polygons)
273  {
274  nItems += line.size() + 1;
275  }
276 
277  outFile
278  << "POLYGONS " << polygons.size() << ' ' << nItems << nl;
279 
280  for (const labelList& line : polygons)
281  {
282  outFile << line.size();
283 
284  for (const label vrt : line)
285  {
286  outFile << ' ' << vrt;
287  }
288  outFile << nl;
289  }
290 
291 
292  outFile
293  << "POINT_DATA " << points.size() << nl
294  << "SCALARS pointID double 1\n"
295  << "LOOKUP_TABLE default\n";
296 
297  forAll(points, i)
298  {
299  outFile << i;
300 
301  if ((i % 10) == 1)
302  {
303  outFile << nl;
304  }
305  else
306  {
307  outFile << ' ';
308  }
309  }
310 
311  if (!pointNormals.empty())
312  {
313  outFile << nl << "NORMALS pointNormals double\n";
314 
315  for(const vector& n : pointNormals)
316  {
317  outFile
318  << float(n.x()) << ' '
319  << float(n.y()) << ' '
320  << float(n.z()) << nl;
321  }
322  }
323 
324  Info<< "End\n" << endl;
325 
326  return 0;
327 }
328 
329 
330 // ************************************************************************* //
static word validate(const std::string &s, const bool prefix=false)
Construct validated word (no invalid characters).
Definition: word.C:39
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:476
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
A line primitive.
Definition: line.H:52
A class for handling file names.
Definition: fileName.H:72
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
Foam::SubStrings splitSpace(const std::string &str, std::string::size_type pos=0)
Split string into sub-strings at whitespace (TAB, NL, VT, FF, CR, SPC)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:600
Output to file stream as an OSstream, normally using std::ofstream for the actual output...
Definition: OFstream.H:71
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:697
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:529
label readLabel(const char *buf)
Parse entire buffer as a label, skipping leading/trailing whitespace.
Definition: label.H:63
static void noParallel()
Remove the parallel options.
Definition: argList.C:598
Sub-ranges of a string with a structure similar to std::match_results, but without the underlying reg...
Definition: SubStrings.H:45
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
const pointField & points
A class for handling words, derived from Foam::string.
Definition: word.H:63
T & emplace_back(Args &&... args)
Construct an element at the end of the list, return reference to the new list element.
Definition: DynamicListI.H:541
Extract command arguments and options from the supplied argc and argv parameters. ...
Definition: argList.H:118
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
static label readObjVertices(const SubStrings &tokens, DynamicList< label > &verts)
Definition: OBJedgeFormat.C:39
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:418
void push_back(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:558
T & emplace_back(Args &&... args)
Construct an element at the end of the list, return reference to the new list element.
Definition: ListI.H:205
T get(const label index) const
Get a value from the argument at index.
Definition: argListI.H:271
#define WarningInFunction
Report a warning using Foam::Warning.
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
Definition: argList.C:365
messageStream Info
Information stream (stdout output on master, null elsewhere)
label n
Foam::argList args(argc, argv)
Namespace for OpenFOAM.