Panda3D
vrmlToEggConverter.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file vrmlToEggConverter.cxx
10  * @author drose
11  * @date 2004-10-01
12  */
13 
14 #include "vrmlToEggConverter.h"
15 #include "vrmlAppearance.h"
16 #include "indexedFaceSet.h"
17 #include "vrmlNodeType.h"
18 #include "parse_vrml.h"
19 #include "vrmlParser.h"
20 #include "eggGroupNode.h"
21 #include "eggGroup.h"
22 #include "eggData.h"
23 #include "deg_2_rad.h"
24 
25 /**
26  *
27  */
28 VRMLToEggConverter::
29 VRMLToEggConverter() {
30 }
31 
32 /**
33  *
34  */
35 VRMLToEggConverter::
36 VRMLToEggConverter(const VRMLToEggConverter &copy) :
38 {
39 }
40 
41 /**
42  *
43  */
44 VRMLToEggConverter::
45 ~VRMLToEggConverter() {
46 }
47 
48 /**
49  * Allocates and returns a new copy of the converter.
50  */
53  return new VRMLToEggConverter(*this);
54 }
55 
56 
57 /**
58  * Returns the English name of the file type this converter supports.
59  */
60 std::string VRMLToEggConverter::
61 get_name() const {
62  return "VRML";
63 }
64 
65 /**
66  * Returns the common extension of the file type this converter supports.
67  */
68 std::string VRMLToEggConverter::
69 get_extension() const {
70  return "wrl";
71 }
72 
73 /**
74  * Returns true if this file type can transparently load compressed files
75  * (with a .pz extension), false otherwise.
76  */
79  return true;
80 }
81 
82 /**
83  * Handles the reading of the input file and converting it to egg. Returns
84  * true if successful, false otherwise.
85  */
87 convert_file(const Filename &filename) {
88  clear_error();
89 
90  VrmlScene *scene = parse_vrml(filename);
91  if (scene == nullptr) {
92  return false;
93  }
94 
95  if (_egg_data->get_coordinate_system() == CS_default) {
96  _egg_data->set_coordinate_system(CS_yup_right);
97  }
98 
99  // First, resolve all the DEFUSE references, and count the number of times
100  // each node is USEd.
101  Nodes nodes;
102  VrmlScene::iterator si;
103  for (si = scene->begin(); si != scene->end(); ++si) {
104  get_all_defs((*si)._node, nodes);
105  }
106 
107  // Now go through the hierarchy again, and this time actually build the egg
108  // structure.
109  VrmlScene::const_iterator csi;
110  for (csi = scene->begin(); csi != scene->end(); ++csi) {
111  vrml_node((*csi)._node, get_egg_data(), LMatrix4d::ident_mat());
112  }
113 
114  return !had_error();
115 }
116 
117 /**
118  * Makes a first pass through the VRML hierarchy, identifying all nodes marked
119  * with a DEF code, and also counting the times each one is referenced by USE.
120  * Later, we'll need this information: if a node is referenced at least once,
121  * we need to define it as an instance node.
122  */
123 void VRMLToEggConverter::
124 get_all_defs(SFNodeRef &vrml, VRMLToEggConverter::Nodes &nodes) {
125  Nodes::iterator ni;
126 
127  switch (vrml._type) {
128  case SFNodeRef::T_def:
129  // If this is a node definition, add it to the map.
130  nassertv(vrml._name != nullptr);
131  nassertv(vrml._p != nullptr);
132  /*
133  This happens too often to bother yelling about it.
134  ni = nodes.find(vrml._name);
135  if (ni != nodes.end()) {
136  cerr << "Warning: node name " << vrml._name
137  << " appears multiple times.\n";
138  }
139  */
140  nodes[vrml._name] = vrml._p;
141  break;
142 
143  case SFNodeRef::T_use:
144  // If it's a reference, resolve it.
145  nassertv(vrml._name != nullptr);
146  ni = nodes.find(vrml._name);
147  if (ni == nodes.end()) {
148  std::cerr << "Unknown node reference: " << vrml._name << "\n";
149  } else {
150  // Increment the use count of the node.
151  (*ni).second->_use_count++;
152 
153  // Store the pointer itself in the reference, so we don't have to do
154  // this again later.
155  vrml._p = (*ni).second;
156  }
157  return;
158 
159  default:
160  break;
161  }
162 
163  VrmlNode *node = vrml._p;
164  if (node != nullptr) {
165  VrmlNode::Fields::iterator fi;
166  for (fi = node->_fields.begin(); fi != node->_fields.end(); ++fi) {
167  if ((*fi)._type->type == SFNODE) {
168  get_all_defs((*fi)._value._sfnode, nodes);
169  } else if ((*fi)._type->type == MFNODE) {
170  MFArray *children = (*fi)._value._mf;
171  MFArray::iterator ci;
172  for (ci = children->begin(); ci != children->end(); ++ci) {
173  get_all_defs((*ci)._sfnode, nodes);
174  }
175  }
176  }
177  }
178 }
179 
180 /**
181  * Processes a single VRML node, converting it to egg and adding it to the egg
182  * file, if appropriate, or doing whatever else should be done.
183  */
184 void VRMLToEggConverter::
185 vrml_node(const SFNodeRef &vrml, EggGroupNode *egg,
186  const LMatrix4d &net_transform) {
187  const VrmlNode *node = vrml._p;
188  if (node != nullptr) {
189  // Now add it to the egg file at this point.
190  if (strcmp(node->_type->getName(), "Group") == 0) {
191  vrml_grouping_node(vrml, egg, net_transform,
192  &VRMLToEggConverter::vrml_group);
193  } else if (strcmp(node->_type->getName(), "Transform") == 0) {
194  vrml_grouping_node(vrml, egg, net_transform,
195  &VRMLToEggConverter::vrml_transform);
196  } else if (strcmp(node->_type->getName(), "Shape") == 0) {
197  vrml_grouping_node(vrml, egg, net_transform,
198  &VRMLToEggConverter::vrml_shape);
199  }
200  }
201 }
202 
203 /**
204  * Begins initial processing of a grouping-type node; that is, any node (like
205  * Group, Transform, or Shape) that maps to a <Group> or <Instance> in egg.
206  * This create the group and does any instance-munging necessary, then calls
207  * the indicated method with the new parameters.
208  */
209 void VRMLToEggConverter::
210 vrml_grouping_node(const SFNodeRef &vrml, EggGroupNode *egg,
211  const LMatrix4d &net_transform,
212  void (VRMLToEggConverter::*process_func)
213  (const VrmlNode *node, EggGroup *group,
214  const LMatrix4d &net_transform)) {
215  const VrmlNode *node = vrml._p;
216  nassertv(node != nullptr);
217  std::string name;
218  if (vrml._name != nullptr) {
219  name = vrml._name;
220  }
221 
222  /*
223  The following code fragment was used in the old DWD-style egg
224  library. Currently, the Panda egg library doesn't support
225  instance references, so we deal with VRML instances by copying.
226 
227  if (vrml._type == SFNodeRef::T_use) {
228  // If this is an instancing reference, just add the reference and return;
229  // no need for further processing on the node.
230  Instances::const_iterator fi = _instances.find(node);
231  assert(fi != _instances.end());
232  EggInstance *inst = _data.CreateInstance(egg);
233  inst->AddGroupRef((*fi).second);
234  return;
235  }
236  */
237 
238  PT(EggGroup) group = new EggGroup(name);
239  egg->add_child(group);
240 
241  LMatrix4d next_transform = net_transform;
242 
243  if (node->_use_count > 0) {
244  // If this node is referenced one or more times later in the file, we must
245  // make it an instance node.
246  group->set_group_type(EggGroup::GT_instance);
247  next_transform = LMatrix4d::ident_mat();
248 
249  // And define the instance for future references. _instances[node] =
250  // group;
251  }
252 
253  (this->*process_func)(node, group, next_transform);
254 }
255 
256 
257 /**
258  * Creates an Egg group corresponding to the VRML group.
259  */
260 void VRMLToEggConverter::
261 vrml_group(const VrmlNode *node, EggGroup *group,
262  const LMatrix4d &net_transform) {
263  const MFArray *children = node->get_value("children")._mf;
264  MFArray::const_iterator ci;
265  for (ci = children->begin(); ci != children->end(); ++ci) {
266  vrml_node((*ci)._sfnode, group, net_transform);
267  }
268 }
269 
270 /**
271  * Creates an Egg group with a transform corresponding to the VRML group.
272  */
273 void VRMLToEggConverter::
274 vrml_transform(const VrmlNode *node, EggGroup *group,
275  const LMatrix4d &net_transform) {
276  const double *scale = node->get_value("scale")._sfvec;
277  const double *rotation = node->get_value("rotation")._sfvec;
278  const double *translation = node->get_value("translation")._sfvec;
279 
280  const double *center = node->get_value("center")._sfvec;
281  const double *o = node->get_value("scaleOrientation")._sfvec;
282 
283  LMatrix4d local_transform = LMatrix4d::ident_mat();
284 
285  bool any_transform = false;
286 
287  if (scale[0] != 1.0 || scale[1] != 1.0 || scale[2] != 1.0) {
288  any_transform = true;
289  if (center[0] != 0.0 || center[1] != 0.0 || center[2] != 0.0) {
290  local_transform *=
291  LMatrix4d::translate_mat(-center[0], -center[1], -center[2]);
292 
293  if (o[3] != 0.0) {
294  local_transform *=
295  LMatrix4d::rotate_mat(rad_2_deg(-o[3]), LVector3d(o[0], o[1], o[2]));
296  local_transform *=
297  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
298  local_transform *=
299  LMatrix4d::rotate_mat(rad_2_deg(o[3]), LVector3d(o[0], o[1], o[2]));
300 
301  } else {
302  local_transform *=
303  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
304  }
305  local_transform *=
306  LMatrix4d::translate_mat(center[0], center[1], center[2]);
307 
308  } else {
309  if (o[3] != 0.0) {
310  local_transform *=
311  LMatrix4d::rotate_mat(rad_2_deg(-o[3]), LVector3d(o[0], o[1], o[2]));
312  local_transform *=
313  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
314  local_transform *=
315  LMatrix4d::rotate_mat(rad_2_deg(o[3]), LVector3d(o[0], o[1], o[2]));
316 
317  } else {
318  local_transform *=
319  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
320  }
321  }
322  }
323 
324  if (rotation[3] != 0.0) {
325  any_transform = true;
326  if (center[0] != 0.0 || center[1] != 0.0 || center[2] != 0.0) {
327  local_transform *=
328  LMatrix4d::translate_mat(-center[0], -center[1], -center[2]);
329  local_transform *=
330  LMatrix4d::rotate_mat(rad_2_deg(rotation[3]),
331  LVector3d(rotation[0], rotation[1], rotation[2]));
332  local_transform *=
333  LMatrix4d::translate_mat(center[0], center[1], center[2]);
334 
335  } else {
336  local_transform *=
337  LMatrix4d::rotate_mat(rad_2_deg(rotation[3]),
338  LVector3d(rotation[0], rotation[1], rotation[2]));
339  }
340  }
341 
342  if (translation[0] != 0.0 ||
343  translation[1] != 0.0 ||
344  translation[2] != 0.0) {
345  any_transform = true;
346  local_transform *=
347  LMatrix4d::translate_mat(translation[0], translation[1], translation[2]);
348  }
349 
350  if (any_transform) {
351  group->set_transform3d(local_transform);
352  }
353 
354  LMatrix4d next_transform = local_transform * net_transform;
355 
356  const MFArray *children = node->get_value("children")._mf;
357  MFArray::const_iterator ci;
358  for (ci = children->begin(); ci != children->end(); ++ci) {
359  vrml_node((*ci)._sfnode, group, next_transform);
360  }
361 }
362 
363 /**
364  * Creates an Egg group corresponding a VRML shape. This will probably
365  * contain a vertex pool and a number of polygons.
366  */
367 void VRMLToEggConverter::
368 vrml_shape(const VrmlNode *node, EggGroup *group,
369  const LMatrix4d &net_transform) {
370  const VrmlNode *geometry = node->get_value("geometry")._sfnode._p;
371 
372  if (geometry != nullptr) {
373  VRMLAppearance appearance(node->get_value("appearance")._sfnode._p);
374 
375  if (strcmp(geometry->_type->getName(), "IndexedFaceSet") == 0) {
376  IndexedFaceSet ifs(geometry, appearance);
377  ifs.convert_to_egg(group, net_transform);
378  } else {
379  std::cerr << "Ignoring " << geometry->_type->getName() << "\n";
380  }
381  }
382 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
bool had_error() const
Returns true if an error was detected during the conversion process (unless _allow_errors is true),...
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
EggData * get_egg_data()
Returns the EggData structure.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VrmlScene * parse_vrml(Filename filename)
Reads the named VRML file and returns a corresponding VrmlScene, or NULL if there is a parse error.
Definition: parse_vrml.cxx:86
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
void clear_error()
Resets the error flag to the no-error state.
void set_transform3d(const LMatrix4d &mat)
Sets the overall transform as a 4x4 matrix.
Definition: eggTransform.I:188
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Decodes the vertices and faces in a VRML indexed face set, and creates the corresponding egg geometry...
This class supervises the construction of an EggData structure from a VRML file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool supports_compressed() const
Returns true if this file type can transparently load compressed files (with a .pz extension),...
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
This is a base class for a family of converter classes that manage a conversion from some file type t...
virtual std::string get_name() const
Returns the English name of the file type this converter supports.