Panda3D
eggToObjConverter.cxx
1 // Filename: eggToObjConverter.cxx
2 // Created by: drose (19Dec12)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "eggToObjConverter.h"
16 #include "config_objegg.h"
17 #include "eggData.h"
18 #include "string_utils.h"
19 #include "streamReader.h"
20 #include "virtualFileSystem.h"
21 #include "eggPolygon.h"
22 #include "eggPoint.h"
23 #include "eggLine.h"
24 #include "dcast.h"
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: EggToObjConverter::Constructor
28 // Access: Public
29 // Description:
30 ////////////////////////////////////////////////////////////////////
31 EggToObjConverter::
32 EggToObjConverter() {
33 }
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: EggToObjConverter::Copy Constructor
37 // Access: Public
38 // Description:
39 ////////////////////////////////////////////////////////////////////
40 EggToObjConverter::
41 EggToObjConverter(const EggToObjConverter &copy) :
43 {
44 }
45 
46 ////////////////////////////////////////////////////////////////////
47 // Function: EggToObjConverter::Destructor
48 // Access: Public
49 // Description:
50 ////////////////////////////////////////////////////////////////////
51 EggToObjConverter::
52 ~EggToObjConverter() {
53 }
54 
55 ////////////////////////////////////////////////////////////////////
56 // Function: EggToObjConverter::make_copy
57 // Access: Public, Virtual
58 // Description: Allocates and returns a new copy of the converter.
59 ////////////////////////////////////////////////////////////////////
62  return new EggToObjConverter(*this);
63 }
64 
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: EggToObjConverter::get_name
68 // Access: Public, Virtual
69 // Description: Returns the English name of the file type this
70 // converter supports.
71 ////////////////////////////////////////////////////////////////////
73 get_name() const {
74  return "obj";
75 }
76 
77 ////////////////////////////////////////////////////////////////////
78 // Function: EggToObjConverter::get_extension
79 // Access: Public, Virtual
80 // Description: Returns the common extension of the file type this
81 // converter supports.
82 ////////////////////////////////////////////////////////////////////
84 get_extension() const {
85  return "obj";
86 }
87 
88 ////////////////////////////////////////////////////////////////////
89 // Function: EggToObjConverter::supports_compressed
90 // Access: Published, Virtual
91 // Description: Returns true if this file type can transparently save
92 // compressed files (with a .pz extension), false
93 // otherwise.
94 ////////////////////////////////////////////////////////////////////
97  return true;
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: EggToObjConverter::write_file
102 // Access: Public, Virtual
103 // Description: Handles the conversion of the internal EggData to the
104 // target file format, written to the specified
105 // filename.
106 ////////////////////////////////////////////////////////////////////
108 write_file(const Filename &filename) {
109  clear_error();
110 
111  if (_egg_data->get_coordinate_system() == CS_default) {
112  _egg_data->set_coordinate_system(CS_zup_right);
113  }
114 
115  if (!process(filename)) {
116  _error = true;
117  }
118  return !had_error();
119 }
120 
121 ////////////////////////////////////////////////////////////////////
122 // Function: EggToObjConverter::process
123 // Access: Private
124 // Description:
125 ////////////////////////////////////////////////////////////////////
126 bool EggToObjConverter::
127 process(const Filename &filename) {
128  _egg_data->flatten_transforms();
129  collect_vertices(_egg_data);
130 
132  Filename obj_filename = Filename::text_filename(filename);
133  vfs->delete_file(obj_filename);
134  ostream *file = vfs->open_write_file(obj_filename, true, true);
135  if (file == (ostream *)NULL) {
136  return false;
137  }
138 
139  _current_group = NULL;
140 
141  /*
142  (*file) << "\n#\n"
143  << "# obj file generated by the following command:\n"
144  << "# " << get_exec_command() << "\n"
145  << "#\n\n";
146  */
147 
148  write_vertices(*file, "v", 3, _unique_vert3);
149  write_vertices(*file, "v", 4, _unique_vert4);
150  write_vertices(*file, "vt", 2, _unique_uv2);
151  write_vertices(*file, "vt", 3, _unique_uv3);
152  write_vertices(*file, "vn", 3, _unique_norm);
153 
154  write_faces(*file, _egg_data);
155 
156  bool success = (file != (ostream *)NULL);
157  vfs->close_write_file(file);
158 
159  return success;
160 }
161 
162 ////////////////////////////////////////////////////////////////////
163 // Function: EggToObjConverter::collect_vertices
164 // Access: Private
165 // Description: Recursively walks the egg structure, looking for
166 // vertices referenced by polygons or points. Any such
167 // vertices are added to the vertex tables for writing
168 // to the obj file.
169 ////////////////////////////////////////////////////////////////////
170 void EggToObjConverter::
171 collect_vertices(EggNode *egg_node) {
172  if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
173  EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node);
174  EggPrimitive::iterator pi;
175  for (pi = egg_prim->begin(); pi != egg_prim->end(); ++pi) {
176  record_vertex(*pi);
177  }
178 
179  } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
180  EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
181 
182  EggGroupNode::iterator ci;
183  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
184  collect_vertices(*ci);
185  }
186  }
187 }
188 
189 ////////////////////////////////////////////////////////////////////
190 // Function: EggToObjConverter::write_faces
191 // Access: Private
192 // Description: Recursively walks the egg structure again, this time
193 // writing out the face records for any polygons,
194 // points, or lines encountered.
195 ////////////////////////////////////////////////////////////////////
196 void EggToObjConverter::
197 write_faces(ostream &out, EggNode *egg_node) {
198  if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
199  const char *prim_type = NULL;
200  if (egg_node->is_of_type(EggPolygon::get_class_type())) {
201  prim_type = "f";
202  } else if (egg_node->is_of_type(EggPoint::get_class_type())) {
203  prim_type = "p";
204  } else if (egg_node->is_of_type(EggLine::get_class_type())) {
205  prim_type = "l";
206  }
207 
208  if (prim_type != NULL) {
209  write_group_reference(out, egg_node);
210 
211  EggPrimitive *egg_prim = DCAST(EggPrimitive, egg_node);
212 
213  out << prim_type;
214  EggPrimitive::iterator pi;
215  for (pi = egg_prim->begin(); pi != egg_prim->end(); ++pi) {
216  VertexDef &vdef = _vmap[(*pi)];
217  int vert_index = -1;
218  int uv_index = -1;
219  int norm_index = -1;
220 
221  if (vdef._vert3_index != -1) {
222  vert_index = vdef._vert3_index + 1;
223  } else if (vdef._vert4_index != -1) {
224  vert_index = vdef._vert4_index + 1 + (int)_unique_vert3.size();
225  }
226 
227  if (vdef._uv2_index != -1) {
228  uv_index = vdef._uv2_index + 1;
229  } else if (vdef._uv3_index != -1) {
230  uv_index = vdef._uv3_index + 1 + (int)_unique_uv2.size();
231  }
232 
233  if (vdef._norm_index != -1) {
234  norm_index = vdef._norm_index + 1;
235  }
236 
237  if (vert_index == -1) {
238  continue;
239  }
240 
241  if (norm_index != -1) {
242  if (uv_index != -1) {
243  out << " " << vert_index << "/" << uv_index << "/" << norm_index;
244  } else {
245  out << " " << vert_index << "//" << norm_index;
246  }
247  } else if (uv_index != -1) {
248  out << " " << vert_index << "/" << uv_index;
249  } else {
250  out << " " << vert_index;
251  }
252  }
253  out << "\n";
254  }
255  } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
256  EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
257 
258  EggGroupNode::iterator ci;
259  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
260  write_faces(out, *ci);
261  }
262  }
263 }
264 
265 ////////////////////////////////////////////////////////////////////
266 // Function: EggToObjConverter::write_group_reference
267 // Access: Private
268 // Description: Writes the "g" tag to describe this polygon's group,
269 // if needed.
270 ////////////////////////////////////////////////////////////////////
271 void EggToObjConverter::
272 write_group_reference(ostream &out, EggNode *egg_node) {
273  EggGroupNode *egg_group = egg_node->get_parent();
274  if (egg_group == _current_group) {
275  // Same group we wrote last time.
276  return;
277  }
278 
279  string group_name;
280  get_group_name(group_name, egg_group);
281  if (group_name.empty()) {
282  out << "g default\n";
283  } else {
284  out << "g" << group_name << "\n";
285  }
286  _current_group = egg_group;
287 }
288 
289 ////////////////////////////////////////////////////////////////////
290 // Function: EggToObjConverter::get_group_name
291 // Access: Private
292 // Description: Recursively determines the appropriate string to
293 // write for the "g" tag to describe a particular
294 // EggGroupNode.
295 ////////////////////////////////////////////////////////////////////
296 void EggToObjConverter::
297 get_group_name(string &group_name, EggGroupNode *egg_group) {
298  string name = trim(egg_group->get_name());
299  if (!name.empty()) {
300  group_name += ' ';
301 
302  // Remove nonstandard characters.
303  for (string::const_iterator ni = name.begin(); ni != name.end(); ++ni) {
304  char c = (*ni);
305  if (!isalnum(c)) {
306  c = '_';
307  }
308  group_name += c;
309  }
310  }
311 
312  // Now recurse.
313  EggGroupNode *egg_parent = egg_group->get_parent();
314  if (egg_parent != NULL) {
315  get_group_name(group_name, egg_parent);
316  }
317 }
318 
319 ////////////////////////////////////////////////////////////////////
320 // Function: EggToObjConverter::record_vertex
321 // Access: Private
322 // Description: Adds the indicated EggVertex to the unique vertex
323 // tables, for writing later by write_vertices().
324 ////////////////////////////////////////////////////////////////////
325 void EggToObjConverter::
326 record_vertex(EggVertex *vertex) {
327  VertexDef &vdef = _vmap[vertex];
328 
329  switch (vertex->get_num_dimensions()) {
330  case 1:
331  vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos1());
332  break;
333  case 2:
334  vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos2());
335  break;
336  case 3:
337  vdef._vert3_index = record_unique(_unique_vert3, vertex->get_pos3());
338  break;
339  case 4:
340  vdef._vert4_index = record_unique(_unique_vert4, vertex->get_pos4());
341  break;
342  }
343 
344  if (vertex->has_uv("")) {
345  vdef._uv2_index = record_unique(_unique_uv2, vertex->get_uv(""));
346  } else if (vertex->has_uvw("")) {
347  vdef._uv3_index = record_unique(_unique_uv3, vertex->get_uvw(""));
348  }
349 
350  if (vertex->has_normal()) {
351  vdef._norm_index = record_unique(_unique_norm, vertex->get_normal());
352  }
353 }
354 
355 ////////////////////////////////////////////////////////////////////
356 // Function: EggToObjConverter::record_unique
357 // Access: Private
358 // Description: Records the indicated vertex value, returning the
359 // shared index if this value already appears elsewhere
360 // in the table, or the new unique index if this is the
361 // first time this value appears.
362 ////////////////////////////////////////////////////////////////////
363 int EggToObjConverter::
364 record_unique(UniqueVertices &unique, const LVecBase4d &vec) {
365  // We record a zero-based index. Note that we will actually write
366  // out a one-based index to the obj file, as required by the
367  // standard.
368  int index = unique.size();
369  UniqueVertices::iterator ui = unique.insert(UniqueVertices::value_type(vec, index)).first;
370  return (*ui).second;
371 }
372 
373 ////////////////////////////////////////////////////////////////////
374 // Function: EggToObjConverter::record_unique
375 // Access: Private
376 // Description: Records the indicated vertex value, returning the
377 // shared index if this value already appears elsewhere
378 // in the table, or the new unique index if this is the
379 // first time this value appears.
380 ////////////////////////////////////////////////////////////////////
381 int EggToObjConverter::
382 record_unique(UniqueVertices &unique, const LVecBase3d &vec) {
383  return record_unique(unique, LVecBase4d(vec[0], vec[1], vec[2], 0.0));
384 }
385 
386 ////////////////////////////////////////////////////////////////////
387 // Function: EggToObjConverter::record_unique
388 // Access: Private
389 // Description: Records the indicated vertex value, returning the
390 // shared index if this value already appears elsewhere
391 // in the table, or the new unique index if this is the
392 // first time this value appears.
393 ////////////////////////////////////////////////////////////////////
394 int EggToObjConverter::
395 record_unique(UniqueVertices &unique, const LVecBase2d &vec) {
396  return record_unique(unique, LVecBase4d(vec[0], vec[1], 0.0, 0.0));
397 }
398 
399 ////////////////////////////////////////////////////////////////////
400 // Function: EggToObjConverter::record_unique
401 // Access: Private
402 // Description: Records the indicated vertex value, returning the
403 // shared index if this value already appears elsewhere
404 // in the table, or the new unique index if this is the
405 // first time this value appears.
406 ////////////////////////////////////////////////////////////////////
407 int EggToObjConverter::
408 record_unique(UniqueVertices &unique, double pos) {
409  return record_unique(unique, LVecBase4d(pos, 0.0, 0.0, 0.0));
410 }
411 
412 ////////////////////////////////////////////////////////////////////
413 // Function: EggToObjConverter::write_vertices
414 // Access: Private
415 // Description: Actually writes the vertex values recorded in the
416 // indicated table to the obj output stream.
417 ////////////////////////////////////////////////////////////////////
418 void EggToObjConverter::
419 write_vertices(ostream &out, const string &prefix, int num_components,
420  const UniqueVertices &unique) {
421  // First, sort the list into numeric order.
422  int num_vertices = (int)unique.size();
423  const LVecBase4d **vertices = (const LVecBase4d **)PANDA_MALLOC_ARRAY(num_vertices * sizeof(LVecBase4d *));
424  memset(vertices, 0, num_vertices * sizeof(LVecBase4d *));
425  UniqueVertices::const_iterator ui;
426  for (ui = unique.begin(); ui != unique.end(); ++ui) {
427  int index = (*ui).second;
428  const LVecBase4d &vec = (*ui).first;
429  nassertv(index >= 0 && index < num_vertices);
430  nassertv(vertices[index] == NULL);
431  vertices[index] = &vec;
432  }
433 
434  for (int i = 0; i < num_vertices; ++i) {
435  out << prefix;
436  const LVecBase4d &vec = *(vertices[i]);
437  for (int ci = 0; ci < num_components; ++ci) {
438  out << " " << vec[ci];
439  }
440  out << "\n";
441  }
442  PANDA_FREE_ARRAY(vertices);
443 }
444 
445 ////////////////////////////////////////////////////////////////////
446 // Function: EggToObjConverter::VertexDef::Constructor
447 // Access: Private
448 // Description:
449 ////////////////////////////////////////////////////////////////////
450 EggToObjConverter::VertexDef::
451 VertexDef() :
452  _vert3_index(-1),
453  _vert4_index(-1),
454  _uv2_index(-1),
455  _uv3_index(-1),
456  _norm_index(-1)
457 {
458 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights...
Definition: eggPrimitive.h:51
bool had_error() const
Returns true if an error was detected during the conversion process, false otherwise.
int get_num_dimensions() const
Returns the number of dimensions the vertex uses.
Definition: eggVertex.I:122
virtual bool supports_compressed() const
Returns true if this file type can transparently save compressed files (with a .pz extension)...
bool has_uvw(const string &name) const
Returns true if the vertex has the named UV coordinate triple, and the named UV coordinate triple is ...
Definition: eggVertex.cxx:142
virtual string get_extension() const
Returns the common extension of the file type this converter supports.
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:1257
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
virtual bool write_file(const Filename &filename)
Handles the conversion of the internal EggData to the target file format, written to the specified fi...
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
Definition: eggVertex.I:222
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:1677
const LTexCoord3d & get_uvw(const string &name) const
Returns the named UV coordinate triple on the vertex.
Definition: eggVertex.cxx:189
LPoint2d get_pos2() const
Only valid if get_num_dimensions() returns 2.
Definition: eggVertex.I:147
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:160
virtual EggToSomethingConverter * make_copy()
Allocates and returns a new copy of the converter.
virtual string get_name() const
Returns the English name of the file type this converter supports.
This is a base class for a family of converter classes that manage a conversion from egg format to so...
void clear_error()
Resets the error flag to the no-error state.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
bool has_uv() const
Returns true if the vertex has an unnamed UV coordinate pair, false otherwise.
Definition: eggVertex.I:194
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:1471
ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written...
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
LPoint4d get_pos4() const
This is always valid, regardless of the value of get_num_dimensions.
Definition: eggVertex.I:178
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
Convert an obj file to egg data.
static void close_write_file(ostream *stream)
Closes a file opened by a previous call to open_write_file().
double get_pos1() const
Only valid if get_num_dimensions() returns 1.
Definition: eggVertex.I:134