Panda3D
vrmlToEggConverter.cxx
1 // Filename: vrmlToEggConverter.cxx
2 // Created by: drose (01Oct04)
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 "vrmlToEggConverter.h"
16 #include "vrmlAppearance.h"
17 #include "indexedFaceSet.h"
18 #include "vrmlNodeType.h"
19 #include "parse_vrml.h"
20 #include "vrmlParser.h"
21 #include "eggGroupNode.h"
22 #include "eggGroup.h"
23 #include "eggData.h"
24 #include "deg_2_rad.h"
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: VRMLToEggConverter::Constructor
28 // Access: Public
29 // Description:
30 ////////////////////////////////////////////////////////////////////
31 VRMLToEggConverter::
32 VRMLToEggConverter() {
33 }
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: VRMLToEggConverter::Copy Constructor
37 // Access: Public
38 // Description:
39 ////////////////////////////////////////////////////////////////////
40 VRMLToEggConverter::
41 VRMLToEggConverter(const VRMLToEggConverter &copy) :
43 {
44 }
45 
46 ////////////////////////////////////////////////////////////////////
47 // Function: VRMLToEggConverter::Destructor
48 // Access: Public
49 // Description:
50 ////////////////////////////////////////////////////////////////////
51 VRMLToEggConverter::
52 ~VRMLToEggConverter() {
53 }
54 
55 ////////////////////////////////////////////////////////////////////
56 // Function: VRMLToEggConverter::make_copy
57 // Access: Public, Virtual
58 // Description: Allocates and returns a new copy of the converter.
59 ////////////////////////////////////////////////////////////////////
62  return new VRMLToEggConverter(*this);
63 }
64 
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: VRMLToEggConverter::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 "VRML";
75 }
76 
77 ////////////////////////////////////////////////////////////////////
78 // Function: VRMLToEggConverter::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 "wrl";
86 }
87 
88 ////////////////////////////////////////////////////////////////////
89 // Function: VRMLToEggConverter::supports_compressed
90 // Access: Published, Virtual
91 // Description: Returns true if this file type can transparently load
92 // compressed files (with a .pz extension), false
93 // otherwise.
94 ////////////////////////////////////////////////////////////////////
97  return true;
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: VRMLToEggConverter::convert_file
102 // Access: Public, Virtual
103 // Description: Handles the reading of the input file and converting
104 // it to egg. Returns true if successful, false
105 // otherwise.
106 ////////////////////////////////////////////////////////////////////
108 convert_file(const Filename &filename) {
109  clear_error();
110 
111  VrmlScene *scene = parse_vrml(filename);
112  if (scene == (VrmlScene *)NULL) {
113  return false;
114  }
115 
116  if (_egg_data->get_coordinate_system() == CS_default) {
117  _egg_data->set_coordinate_system(CS_yup_right);
118  }
119 
120  // First, resolve all the DEF/USE references, and count the number
121  // of times each node is USEd.
122  Nodes nodes;
123  VrmlScene::iterator si;
124  for (si = scene->begin(); si != scene->end(); ++si) {
125  get_all_defs((*si)._node, nodes);
126  }
127 
128  // Now go through the hierarchy again, and this time actually
129  // build the egg structure.
130  VrmlScene::const_iterator csi;
131  for (csi = scene->begin(); csi != scene->end(); ++csi) {
132  vrml_node((*csi)._node, get_egg_data(), LMatrix4d::ident_mat());
133  }
134 
135  return !had_error();
136 }
137 
138 ////////////////////////////////////////////////////////////////////
139 // Function: VRMLToEggConverter::get_all_defs
140 // Access: Private
141 // Description: Makes a first pass through the VRML hierarchy,
142 // identifying all nodes marked with a DEF code, and
143 // also counting the times each one is referenced by
144 // USE. Later, we'll need this information: if a node
145 // is referenced at least once, we need to define it as
146 // an instance node.
147 ////////////////////////////////////////////////////////////////////
148 void VRMLToEggConverter::
149 get_all_defs(SFNodeRef &vrml, VRMLToEggConverter::Nodes &nodes) {
150  Nodes::iterator ni;
151 
152  switch (vrml._type) {
153  case SFNodeRef::T_def:
154  // If this is a node definition, add it to the map.
155  nassertv(vrml._name != NULL);
156  nassertv(vrml._p != NULL);
157  /*
158  This happens too often to bother yelling about it.
159  ni = nodes.find(vrml._name);
160  if (ni != nodes.end()) {
161  cerr << "Warning: node name " << vrml._name
162  << " appears multiple times.\n";
163  }
164  */
165  nodes[vrml._name] = vrml._p;
166  break;
167 
168  case SFNodeRef::T_use:
169  // If it's a reference, resolve it.
170  nassertv(vrml._name != NULL);
171  ni = nodes.find(vrml._name);
172  if (ni == nodes.end()) {
173  cerr << "Unknown node reference: " << vrml._name << "\n";
174  } else {
175  // Increment the use count of the node.
176  (*ni).second->_use_count++;
177 
178  // Store the pointer itself in the reference, so we don't have
179  // to do this again later.
180  vrml._p = (*ni).second;
181  }
182  return;
183 
184  default:
185  break;
186  }
187 
188  VrmlNode *node = vrml._p;
189  if (node != NULL) {
190  VrmlNode::Fields::iterator fi;
191  for (fi = node->_fields.begin(); fi != node->_fields.end(); ++fi) {
192  if ((*fi)._type->type == SFNODE) {
193  get_all_defs((*fi)._value._sfnode, nodes);
194  } else if ((*fi)._type->type == MFNODE) {
195  MFArray *children = (*fi)._value._mf;
196  MFArray::iterator ci;
197  for (ci = children->begin(); ci != children->end(); ++ci) {
198  get_all_defs((*ci)._sfnode, nodes);
199  }
200  }
201  }
202  }
203 }
204 
205 ////////////////////////////////////////////////////////////////////
206 // Function: VRMLToEggConverter::vrml_node
207 // Access: Public
208 // Description: Processes a single VRML node, converting it to egg
209 // and adding it to the egg file, if appropriate, or
210 // doing whatever else should be done.
211 ////////////////////////////////////////////////////////////////////
212 void VRMLToEggConverter::
213 vrml_node(const SFNodeRef &vrml, EggGroupNode *egg,
214  const LMatrix4d &net_transform) {
215  const VrmlNode *node = vrml._p;
216  if (node != NULL) {
217  // Now add it to the egg file at this point.
218  if (strcmp(node->_type->getName(), "Group") == 0) {
219  vrml_grouping_node(vrml, egg, net_transform,
220  &VRMLToEggConverter::vrml_group);
221  } else if (strcmp(node->_type->getName(), "Transform") == 0) {
222  vrml_grouping_node(vrml, egg, net_transform,
223  &VRMLToEggConverter::vrml_transform);
224  } else if (strcmp(node->_type->getName(), "Shape") == 0) {
225  vrml_grouping_node(vrml, egg, net_transform,
226  &VRMLToEggConverter::vrml_shape);
227  }
228  }
229 }
230 
231 ////////////////////////////////////////////////////////////////////
232 // Function: VRMLToEggConverter::vrml_grouping_node
233 // Access: Public
234 // Description: Begins initial processing of a grouping-type node;
235 // that is, any node (like Group, Transform, or Shape)
236 // that maps to a <Group> or <Instance> in egg. This
237 // create the group and does any instance-munging
238 // necessary, then calls the indicated method with the
239 // new parameters.
240 ////////////////////////////////////////////////////////////////////
241 void VRMLToEggConverter::
242 vrml_grouping_node(const SFNodeRef &vrml, EggGroupNode *egg,
243  const LMatrix4d &net_transform,
244  void (VRMLToEggConverter::*process_func)
245  (const VrmlNode *node, EggGroup *group,
246  const LMatrix4d &net_transform)) {
247  const VrmlNode *node = vrml._p;
248  nassertv(node != NULL);
249  string name;
250  if (vrml._name != NULL) {
251  name = vrml._name;
252  }
253 
254  /*
255  The following code fragment was used in the old DWD-style egg
256  library. Currently, the Panda egg library doesn't support
257  instance references, so we deal with VRML instances by copying.
258 
259  if (vrml._type == SFNodeRef::T_use) {
260  // If this is an instancing reference, just add the reference and
261  // return; no need for further processing on the node.
262  Instances::const_iterator fi = _instances.find(node);
263  assert(fi != _instances.end());
264  EggInstance *inst = _data.CreateInstance(egg);
265  inst->AddGroupRef((*fi).second);
266  return;
267  }
268  */
269 
270  PT(EggGroup) group = new EggGroup(name);
271  egg->add_child(group);
272 
273  LMatrix4d next_transform = net_transform;
274 
275  if (node->_use_count > 0) {
276  // If this node is referenced one or more times later in the file,
277  // we must make it an instance node.
278  group->set_group_type(EggGroup::GT_instance);
279  next_transform = LMatrix4d::ident_mat();
280 
281  // And define the instance for future references.
282  // _instances[node] = group;
283  }
284 
285  (this->*process_func)(node, group, next_transform);
286 }
287 
288 
289 ////////////////////////////////////////////////////////////////////
290 // Function: VRMLToEggConverter::vrml_group
291 // Access: Public
292 // Description: Creates an Egg group corresponding to the VRML group.
293 ////////////////////////////////////////////////////////////////////
294 void VRMLToEggConverter::
295 vrml_group(const VrmlNode *node, EggGroup *group,
296  const LMatrix4d &net_transform) {
297  const MFArray *children = node->get_value("children")._mf;
298  MFArray::const_iterator ci;
299  for (ci = children->begin(); ci != children->end(); ++ci) {
300  vrml_node((*ci)._sfnode, group, net_transform);
301  }
302 }
303 
304 ////////////////////////////////////////////////////////////////////
305 // Function: VRMLToEggConverter::vrml_transform
306 // Access: Public
307 // Description: Creates an Egg group with a transform corresponding
308 // to the VRML group.
309 ////////////////////////////////////////////////////////////////////
310 void VRMLToEggConverter::
311 vrml_transform(const VrmlNode *node, EggGroup *group,
312  const LMatrix4d &net_transform) {
313  const double *scale = node->get_value("scale")._sfvec;
314  const double *rotation = node->get_value("rotation")._sfvec;
315  const double *translation = node->get_value("translation")._sfvec;
316 
317  const double *center = node->get_value("center")._sfvec;
318  const double *o = node->get_value("scaleOrientation")._sfvec;
319 
320  LMatrix4d local_transform = LMatrix4d::ident_mat();
321 
322  bool any_transform = false;
323 
324  if (scale[0] != 1.0 || scale[1] != 1.0 || scale[2] != 1.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 
330  if (o[3] != 0.0) {
331  local_transform *=
332  LMatrix4d::rotate_mat(rad_2_deg(-o[3]), LVector3d(o[0], o[1], o[2]));
333  local_transform *=
334  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
335  local_transform *=
336  LMatrix4d::rotate_mat(rad_2_deg(o[3]), LVector3d(o[0], o[1], o[2]));
337 
338  } else {
339  local_transform *=
340  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
341  }
342  local_transform *=
343  LMatrix4d::translate_mat(center[0], center[1], center[2]);
344 
345  } else {
346  if (o[3] != 0.0) {
347  local_transform *=
348  LMatrix4d::rotate_mat(rad_2_deg(-o[3]), LVector3d(o[0], o[1], o[2]));
349  local_transform *=
350  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
351  local_transform *=
352  LMatrix4d::rotate_mat(rad_2_deg(o[3]), LVector3d(o[0], o[1], o[2]));
353 
354  } else {
355  local_transform *=
356  LMatrix4d::scale_mat(scale[0], scale[1], scale[2]);
357  }
358  }
359  }
360 
361  if (rotation[3] != 0.0) {
362  any_transform = true;
363  if (center[0] != 0.0 || center[1] != 0.0 || center[2] != 0.0) {
364  local_transform *=
365  LMatrix4d::translate_mat(-center[0], -center[1], -center[2]);
366  local_transform *=
367  LMatrix4d::rotate_mat(rad_2_deg(rotation[3]),
368  LVector3d(rotation[0], rotation[1], rotation[2]));
369  local_transform *=
370  LMatrix4d::translate_mat(center[0], center[1], center[2]);
371 
372  } else {
373  local_transform *=
374  LMatrix4d::rotate_mat(rad_2_deg(rotation[3]),
375  LVector3d(rotation[0], rotation[1], rotation[2]));
376  }
377  }
378 
379  if (translation[0] != 0.0 ||
380  translation[1] != 0.0 ||
381  translation[2] != 0.0) {
382  any_transform = true;
383  local_transform *=
384  LMatrix4d::translate_mat(translation[0], translation[1], translation[2]);
385  }
386 
387  if (any_transform) {
388  group->set_transform3d(local_transform);
389  }
390 
391  LMatrix4d next_transform = local_transform * net_transform;
392 
393  const MFArray *children = node->get_value("children")._mf;
394  MFArray::const_iterator ci;
395  for (ci = children->begin(); ci != children->end(); ++ci) {
396  vrml_node((*ci)._sfnode, group, next_transform);
397  }
398 }
399 
400 ////////////////////////////////////////////////////////////////////
401 // Function: VRMLToEggConverter::vrml_shape
402 // Access: Public
403 // Description: Creates an Egg group corresponding a VRML shape.
404 // This will probably contain a vertex pool and a number
405 // of polygons.
406 ////////////////////////////////////////////////////////////////////
407 void VRMLToEggConverter::
408 vrml_shape(const VrmlNode *node, EggGroup *group,
409  const LMatrix4d &net_transform) {
410  const VrmlNode *geometry = node->get_value("geometry")._sfnode._p;
411 
412  if (geometry != NULL) {
413  VRMLAppearance appearance(node->get_value("appearance")._sfnode._p);
414 
415  if (strcmp(geometry->_type->getName(), "IndexedFaceSet") == 0) {
416  IndexedFaceSet ifs(geometry, appearance);
417  ifs.convert_to_egg(group, net_transform);
418  } else {
419  cerr << "Ignoring " << geometry->_type->getName() << "\n";
420  }
421  }
422 }
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
virtual string get_extension() const
Returns the common extension of the file type this converter supports.
static LMatrix4d rotate_mat(double angle, const LVecBase3d &axis, CoordinateSystem cs=CS_default)
Returns a matrix that rotates by the given angle in degrees counterclockwise about the indicated vect...
Definition: lmatrix.h:6690
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:51
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
static LMatrix4d scale_mat(const LVecBase3d &scale)
Returns a matrix that applies the indicated scale in each of the three axes.
Definition: lmatrix.h:6721
EggData * get_egg_data()
Returns the EggData structure.
static LMatrix4d translate_mat(const LVecBase3d &trans)
Returns a matrix that applies the indicated translation.
Definition: lmatrix.h:6662
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:36
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:222
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static const LMatrix4d & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:5168
Decodes the vertices and faces in a VRML indexed face set, and creates the corresponding egg geometry...
virtual string get_name() const
Returns the English name of the file type this converter supports.
This class supervises the construction of an EggData structure from a VRML file.
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:760
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.
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...