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  */
52 make_copy() {
53  return new VRMLToEggConverter(*this);
54 }
55 
56 
57 /**
58  * Returns the English name of the file type this converter supports.
59  */
61 get_name() const {
62  return "VRML";
63 }
64 
65 /**
66  * Returns the common extension of the file type this converter supports.
67  */
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  */
78 supports_compressed() const {
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 }
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
void set_transform3d(const LMatrix4d &mat)
Sets the overall transform as a 4x4 matrix.
Definition: eggTransform.I:188
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Decodes the vertices and faces in a VRML indexed face set, and creates the corresponding egg geometry...
This is a base class for a family of converter classes that manage a conversion from some file type t...
bool had_error() const
Returns true if an error was detected during the conversion process (unless _allow_errors is true),...
EggData * get_egg_data()
Returns the EggData structure.
void clear_error()
Resets the error flag to the no-error state.
This class supervises the construction of an EggData structure from a VRML file.
virtual std::string get_name() const
Returns the English name of the file type this converter supports.
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
virtual bool supports_compressed() const
Returns true if this file type can transparently load compressed files (with a .pz extension),...
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.