Panda3D
eggSaver.cxx
1 // Filename: eggSaver.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 "eggSaver.h"
16 
17 #include "pandaNode.h"
18 #include "workingNodePath.h"
19 #include "nodePath.h"
20 #include "billboardEffect.h"
21 #include "renderEffects.h"
22 #include "transformState.h"
23 #include "colorScaleAttrib.h"
24 #include "colorAttrib.h"
25 #include "materialAttrib.h"
26 #include "textureAttrib.h"
27 #include "cullFaceAttrib.h"
28 #include "transparencyAttrib.h"
29 #include "depthWriteAttrib.h"
30 #include "lodNode.h"
31 #include "switchNode.h"
32 #include "sequenceNode.h"
33 #include "collisionNode.h"
34 #include "collisionPolygon.h"
35 #include "collisionPlane.h"
36 #include "collisionSphere.h"
37 #include "collisionBox.h"
38 #include "collisionInvSphere.h"
39 #include "collisionTube.h"
40 #include "textureStage.h"
41 #include "geomNode.h"
42 #include "geom.h"
43 #include "geomTriangles.h"
44 #include "geomPatches.h"
45 #include "geomPoints.h"
46 #include "geomLines.h"
47 #include "geomVertexReader.h"
48 #include "transformTable.h"
49 #include "modelNode.h"
50 #include "animBundleNode.h"
51 #include "animChannelMatrixXfmTable.h"
52 #include "characterJoint.h"
53 #include "character.h"
54 #include "string_utils.h"
55 #include "bamFile.h"
56 #include "bamCacheRecord.h"
57 #include "eggSAnimData.h"
58 #include "eggXfmAnimData.h"
59 #include "eggXfmSAnim.h"
60 #include "eggGroup.h"
61 #include "eggVertexPool.h"
62 #include "eggVertex.h"
63 #include "eggPrimitive.h"
64 #include "eggPolygon.h"
65 #include "eggPatch.h"
66 #include "eggPoint.h"
67 #include "eggLine.h"
68 #include "eggTexture.h"
69 #include "eggMaterial.h"
70 #include "eggRenderMode.h"
71 #include "eggTable.h"
72 #include "dcast.h"
73 
74 ////////////////////////////////////////////////////////////////////
75 // Function: EggSaver::Constructor
76 // Access: Published
77 // Description:
78 ////////////////////////////////////////////////////////////////////
79 EggSaver::
80 EggSaver(EggData *data) :
81  _data(data)
82 {
83  if (_data == NULL) {
84  _data = new EggData;
85  }
86 }
87 
88 ////////////////////////////////////////////////////////////////////
89 // Function: EggSaver::add_node
90 // Access: Published
91 // Description: Adds the scene graph rooted at the indicated node to
92 // the accumulated egg data within this object. Call
93 // get_egg_data() to retrieve the result.
94 ////////////////////////////////////////////////////////////////////
95 void EggSaver::
97  _vpool = new EggVertexPool(node->get_name());
98  _data->add_child(_vpool);
99 
100  NodePath root(node);
101  convert_node(WorkingNodePath(root), _data, false);
102 
103  // Remove the vertex pool if it has no vertices.
104  if (_vpool->empty()) {
105  _data->remove_child(_vpool);
106  }
107  _vpool = NULL;
108 }
109 
110 ////////////////////////////////////////////////////////////////////
111 // Function: EggSaver::convert_node
112 // Access: Private
113 // Description: Converts the indicated node to the corresponding Egg
114 // constructs, by first determining what kind of node it
115 // is.
116 ////////////////////////////////////////////////////////////////////
117 void EggSaver::
118 convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent,
119  bool has_decal) {
120  PandaNode *node = node_path.node();
121  if (node->is_geom_node()) {
122  convert_geom_node(DCAST(GeomNode, node), node_path, egg_parent, has_decal);
123 
124  } else if (node->is_of_type(LODNode::get_class_type())) {
125  convert_lod_node(DCAST(LODNode, node), node_path, egg_parent, has_decal);
126 
127  } else if (node->is_of_type(SequenceNode::get_class_type())) {
128  convert_sequence_node(DCAST(SequenceNode, node), node_path, egg_parent, has_decal);
129 
130  } else if (node->is_of_type(SwitchNode::get_class_type())) {
131  convert_switch_node(DCAST(SwitchNode, node), node_path, egg_parent, has_decal);
132 
133  } else if (node->is_of_type(CollisionNode::get_class_type())) {
134  convert_collision_node(DCAST(CollisionNode, node), node_path, egg_parent, has_decal);
135 
136  } else if (node->is_of_type(AnimBundleNode::get_class_type())) {
137  convert_anim_node(DCAST(AnimBundleNode, node), node_path, egg_parent, has_decal);
138 
139  } else if (node->is_of_type(Character::get_class_type())) {
140  convert_character_node(DCAST(Character, node), node_path, egg_parent, has_decal);
141 
142  } else {
143  // Just a generic node.
144  EggGroup *egg_group = new EggGroup(node->get_name());
145  egg_parent->add_child(egg_group);
146  apply_node_properties(egg_group, node);
147 
148  recurse_nodes(node_path, egg_group, has_decal);
149  }
150 }
151 
152 ////////////////////////////////////////////////////////////////////
153 // Function: EggSaver::convert_lod_node
154 // Access: Private
155 // Description: Converts the indicated LODNode to the corresponding
156 // Egg constructs.
157 ////////////////////////////////////////////////////////////////////
158 void EggSaver::
159 convert_lod_node(LODNode *node, const WorkingNodePath &node_path,
160  EggGroupNode *egg_parent, bool has_decal) {
161  // An LOD node gets converted to an ordinary EggGroup, but we apply
162  // the appropriate switch conditions to each of our children.
163  EggGroup *egg_group = new EggGroup(node->get_name());
164  egg_parent->add_child(egg_group);
165  apply_node_properties(egg_group, node);
166 
167  int num_children = node->get_num_children();
168  int num_switches = node->get_num_switches();
169 
170  num_children = min(num_children, num_switches);
171 
172  for (int i = 0; i < num_children; i++) {
173  PandaNode *child = node->get_child(i);
174 
175  // Convert just this one node to an EggGroup.
176  PT(EggGroup) next_group = new EggGroup;
177  convert_node(WorkingNodePath(node_path, child), next_group, has_decal);
178 
179  if (next_group->size() == 1) {
180  // If we have exactly one child, and that child is an EggGroup,
181  // collapse.
182  EggNode *child_node = *next_group->begin();
183  if (child_node->is_of_type(EggGroup::get_class_type())) {
184  PT(EggGroup) child = DCAST(EggGroup, child_node);
185  next_group->remove_child(child.p());
186  next_group = child;
187  }
188  }
189 
190  // Now set up the switching properties appropriately.
191  PN_stdfloat in = node->get_in(i);
192  PN_stdfloat out = node->get_out(i);
193  LPoint3 center = node->get_center();
194  EggSwitchConditionDistance dist(in, out, LCAST(double, center));
195  next_group->set_lod(dist);
196  egg_group->add_child(next_group.p());
197  }
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: EggSaver::convert_sequence_node
202 // Access: Private
203 // Description: Converts the indicated SequenceNode to the corresponding
204 // Egg constructs.
205 ////////////////////////////////////////////////////////////////////
206 void EggSaver::
207 convert_sequence_node(SequenceNode *node, const WorkingNodePath &node_path,
208  EggGroupNode *egg_parent, bool has_decal) {
209  // A sequence node gets converted to an ordinary EggGroup, we only apply
210  // the appropriate switch attributes to turn it into a sequence
211  EggGroup *egg_group = new EggGroup(node->get_name());
212  egg_parent->add_child(egg_group);
213  apply_node_properties(egg_group, node);
214 
215  // turn it into a sequence with the right frame-rate
216  egg_group->set_switch_flag(true);
217  egg_group->set_switch_fps(node->get_frame_rate());
218 
219  int num_children = node->get_num_children();
220 
221  for (int i = 0; i < num_children; i++) {
222  PandaNode *child = node->get_child(i);
223 
224  // Convert just this one node to an EggGroup.
225  PT(EggGroup) next_group = new EggGroup;
226  convert_node(WorkingNodePath(node_path, child), next_group, has_decal);
227 
228  egg_group->add_child(next_group.p());
229  }
230 
231 }
232 
233 ////////////////////////////////////////////////////////////////////
234 // Function: EggSaver::convert_switch_node
235 // Access: Private
236 // Description: Converts the indicated SwitchNode to the corresponding
237 // Egg constructs.
238 ////////////////////////////////////////////////////////////////////
239 void EggSaver::
240 convert_switch_node(SwitchNode *node, const WorkingNodePath &node_path,
241  EggGroupNode *egg_parent, bool has_decal) {
242  // A sequence node gets converted to an ordinary EggGroup, we only apply
243  // the appropriate switch attributes to turn it into a sequence
244  EggGroup *egg_group = new EggGroup(node->get_name());
245  egg_parent->add_child(egg_group);
246  apply_node_properties(egg_group, node);
247 
248  // turn it into a switch..
249  egg_group->set_switch_flag(true);
250 
251  int num_children = node->get_num_children();
252 
253  for (int i = 0; i < num_children; i++) {
254  PandaNode *child = node->get_child(i);
255 
256  // Convert just this one node to an EggGroup.
257  PT(EggGroup) next_group = new EggGroup;
258  convert_node(WorkingNodePath(node_path, child), next_group, has_decal);
259 
260  egg_group->add_child(next_group.p());
261  }
262 }
263 
264 ////////////////////////////////////////////////////////////////////
265 // Function: EggSaver::convert_animGroup_node
266 // Access: Private
267 // Description: Converts the indicated AnimationGroupNodes to the corresponding
268 // Egg constructs.
269 ////////////////////////////////////////////////////////////////////
270 EggGroupNode * EggSaver::convert_animGroup_node(AnimGroup *animGroup, double fps ) {
271  int num_children = animGroup->get_num_children();
272 
273  EggGroupNode *eggNode = NULL;
274  if (animGroup->is_of_type(AnimBundle::get_class_type())) {
275  EggTable *eggTable = new EggTable(animGroup->get_name());
276  eggTable ->set_table_type(EggTable::TT_bundle);
277  eggNode = eggTable;
278  } else if (animGroup->is_of_type(AnimGroup::get_class_type())) {
279  EggTable *eggTable = new EggTable(animGroup->get_name());
280  eggTable ->set_table_type(EggTable::TT_table);
281  eggNode = eggTable;
282  }
283 
284  if (animGroup->is_of_type(AnimChannelMatrixXfmTable::get_class_type())) {
285  AnimChannelMatrixXfmTable *xmfTable = DCAST(AnimChannelMatrixXfmTable, animGroup);
286  EggXfmSAnim *egg_anim = new EggXfmSAnim("xform");
287  egg_anim->set_fps(fps);
288  for (int i = 0; i < num_matrix_components; i++) {
289  string componentName(1, matrix_component_letters[i]);
290  char table_id = matrix_component_letters[i];
291  CPTA_stdfloat table = xmfTable->get_table(table_id);
292 
293  if (xmfTable->has_table(table_id)) {
294  for (unsigned int j = 0; j < table.size(); j++) {
295  egg_anim->add_component_data(componentName, table[(int)j]);
296  }
297  }
298  }
299  eggNode->add_child(egg_anim);
300  }
301  for (int i = 0; i < num_children; i++) {
302  AnimGroup *animChild = animGroup->get_child(i);
303  EggGroupNode *eggChildNode = convert_animGroup_node(animChild, fps);
304  if (eggChildNode!=NULL) {
305  nassertr(eggNode!=NULL, NULL);
306  eggNode->add_child(eggChildNode);
307  }
308  }
309  return eggNode;
310 }
311 
312 ////////////////////////////////////////////////////////////////////
313 // Function: EggSaver::convert_anim_node
314 // Access: Private
315 // Description: Converts the indicated AnimNode to the corresponding
316 // Egg constructs.
317 ////////////////////////////////////////////////////////////////////
318 void EggSaver::
319 convert_anim_node(AnimBundleNode *node, const WorkingNodePath &node_path,
320  EggGroupNode *egg_parent, bool has_decal) {
321 
322  // A sequence node gets converted to an ordinary EggGroup, we only apply
323  // the appropriate switch attributes to turn it into a sequence
324  EggTable *eggTable = new EggTable();
325  //egg_parent->add_child(eggTable);
326  _data->add_child(eggTable);
327 
328  AnimBundle *animBundle = node->get_bundle();
329  // turn it into a switch..
330  //egg_group->set_switch_flag(true);
331 
332  EggGroupNode *eggAnimation = convert_animGroup_node(animBundle, animBundle->get_base_frame_rate());
333  eggTable->add_child(eggAnimation);
334 }
335 
336 ////////////////////////////////////////////////////////////////////
337 // Function: EggSaver::convert_character_bundle
338 // Access: Private
339 // Description: Converts the indicated Character Bundle to the corresponding
340 // Egg joints structure.
341 ////////////////////////////////////////////////////////////////////
342 void EggSaver::
343 convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, CharacterJointMap *jointMap) {
344  int num_children = bundleNode->get_num_children();
345 
346  EggGroupNode *joint_group = egg_parent;
347  if (bundleNode->is_of_type(CharacterJoint::get_class_type())) {
348  CharacterJoint *character_joint = DCAST(CharacterJoint, bundleNode);
349 
350  LMatrix4 transformf;
351  character_joint->get_transform(transformf);
352  LMatrix4d transformd(LCAST(double, transformf));
353  EggGroup *joint = new EggGroup(bundleNode->get_name());
354  joint->add_matrix4(transformd);
355  joint->set_group_type(EggGroup::GT_joint);
356  joint_group = joint;
357  egg_parent->add_child(joint_group);
358  if (jointMap!=NULL) {
359  CharacterJointMap::iterator mi = jointMap->find(character_joint);
360  if (mi != jointMap->end()) {
361  pvector<pair<EggVertex*,PN_stdfloat> > &joint_vertices = (*mi).second;
362  pvector<pair<EggVertex*,PN_stdfloat> >::const_iterator vi;
363  for (vi = joint_vertices.begin(); vi != joint_vertices.end(); ++vi) {
364  joint->set_vertex_membership((*vi).first, (*vi).second);
365  }
366  }
367  }
368  }
369 
370  for (int i = 0; i < num_children ; i++) {
371  PartGroup *partGroup= bundleNode->get_child(i);
372  convert_character_bundle(partGroup, joint_group, jointMap);
373  }
374 
375 }
376 
377 ////////////////////////////////////////////////////////////////////
378 // Function: EggSaver::convert_character_node
379 // Access: Private
380 // Description: Converts the indicated Character to the corresponding
381 // Egg constructs.
382 ////////////////////////////////////////////////////////////////////
383 void EggSaver::
384 convert_character_node(Character *node, const WorkingNodePath &node_path,
385  EggGroupNode *egg_parent, bool has_decal) {
386 
387  // A sequence node gets converted to an ordinary EggGroup, we only apply
388  // the appropriate switch attributes to turn it into a sequence
389  EggGroup *egg_group = new EggGroup(node->get_name());
390  egg_group->set_dart_type(EggGroup::DT_default);
391  egg_parent->add_child(egg_group);
392  apply_node_properties(egg_group, node);
393 
394  CharacterJointMap jointMap;
395 
396  // turn it into a switch..
397  //egg_group->set_switch_flag(true);
398 
399  int num_children = node->get_num_children();
400  int num_bundles = node->get_num_bundles();
401 
402  for (int i = 0; i < num_children; i++) {
403  PandaNode *child = node->get_child(i);
404 
405  if (child->is_geom_node()) {
406  convert_geom_node(DCAST(GeomNode, child), WorkingNodePath(node_path, child), egg_group, has_decal, &jointMap);
407  }
408  }
409 
410  for (int i = 0; i < num_bundles ; i++) {
411  PartBundle *bundle= node->get_bundle(i);
412  convert_character_bundle(bundle, egg_group, &jointMap);
413  }
414 
415 }
416 
417 
418 ////////////////////////////////////////////////////////////////////
419 // Function: EggSaver::convert_collision_node
420 // Access: Private
421 // Description: Converts the indicated CollisionNode to the corresponding
422 // Egg constructs.
423 ////////////////////////////////////////////////////////////////////
424 void EggSaver::
425 convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path,
426  EggGroupNode *egg_parent, bool has_decal) {
427  // A sequence node gets converted to an ordinary EggGroup, we only apply
428  // the appropriate switch attributes to turn it into a sequence
429  EggGroup *egg_group = new EggGroup(node->get_name());
430  egg_parent->add_child(egg_group);
431  apply_node_properties(egg_group, node, false);
432 
433  // turn it into a collision node
434  egg_group->set_collide_flags(EggGroup::CF_descend);
435 
436  NodePath np = node_path.get_node_path();
437  CPT(TransformState) net_transform = np.get_net_transform();
438  LMatrix4 net_mat = net_transform->get_mat();
439  LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv());
440  net_mat = net_mat * inv;
441 
442  int num_solids = node->get_num_solids();
443 
444  if (num_solids > 0) {
445  // create vertex pool for collisions
446  EggVertexPool *cvpool = new EggVertexPool("vpool-collision");
447  egg_group->add_child(cvpool);
448 
449  // traverse solids
450  for (int i = 0; i < num_solids; i++) {
451  CPT(CollisionSolid) child = node->get_solid(i);
452  int flags = EggGroup::CF_descend;
453 
454  if (!child->is_tangible()) {
455  flags |= EggGroup::CF_intangible;
456  }
457 
458  if (child->has_effective_normal() &&
459  child->get_effective_normal() == LVector3::up()) {
460  flags |= EggGroup::CF_level;
461  }
462 
463  if (child->is_of_type(CollisionPolygon::get_class_type())) {
464  egg_group->set_cs_type(EggGroup::CST_polyset);
465  egg_group->set_collide_flags(flags);
466 
467  EggPolygon *egg_poly = new EggPolygon;
468  egg_group->add_child(egg_poly);
469 
470  CPT(CollisionPolygon) poly = DCAST(CollisionPolygon, child);
471  int num_points = poly->get_num_points();
472  for (int j = 0; j < num_points; j++) {
473  EggVertex egg_vert;
474  egg_vert.set_pos(LCAST(double, poly->get_point(j) * net_mat));
475  egg_vert.set_normal(LCAST(double, poly->get_normal() * net_mat));
476 
477  EggVertex *new_egg_vert = cvpool->create_unique_vertex(egg_vert);
478  egg_poly->add_vertex(new_egg_vert);
479  }
480 
481  } else if (child->is_of_type(CollisionSphere::get_class_type())) {
482  CPT(CollisionSphere) sphere = DCAST(CollisionSphere, child);
483  LPoint3 center = sphere->get_center();
484  PN_stdfloat radius = sphere->get_radius();
485 
486  EggGroup *egg_sphere;
487  if (num_solids == 1) {
488  egg_sphere = egg_group;
489  } else {
490  egg_sphere = new EggGroup;
491  egg_group->add_child(egg_sphere);
492  }
493 
494  if (child->is_of_type(CollisionInvSphere::get_class_type())) {
495  egg_sphere->set_cs_type(EggGroup::CST_inv_sphere);
496  } else {
497  egg_sphere->set_cs_type(EggGroup::CST_sphere);
498  }
499  egg_sphere->set_collide_flags(flags);
500 
501  EggVertex ev1, ev2, ev3, ev4;
502  ev1.set_pos(LCAST(double, (center + LVector3(radius, 0, 0)) * net_mat));
503  ev2.set_pos(LCAST(double, (center + LVector3(0, radius, 0)) * net_mat));
504  ev3.set_pos(LCAST(double, (center + LVector3(-radius, 0, 0)) * net_mat));
505  ev4.set_pos(LCAST(double, (center + LVector3(0, -radius, 0)) * net_mat));
506 
507  EggPolygon *egg_poly = new EggPolygon;
508  egg_sphere->add_child(egg_poly);
509 
510  egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
511  egg_poly->add_vertex(cvpool->create_unique_vertex(ev2));
512  egg_poly->add_vertex(cvpool->create_unique_vertex(ev3));
513  egg_poly->add_vertex(cvpool->create_unique_vertex(ev4));
514 
515  } else if (child->is_of_type(CollisionPlane::get_class_type())) {
516  LPlane plane = DCAST(CollisionPlane, child)->get_plane();
517  LPoint3 origin = plane.get_point();
518  LVector3 normal = plane.get_normal();
519 
520  // Get an arbitrary vector on the plane by taking the cross product
521  // with any vector, as long as it is different.
522  LVector3 vec1;
523  if (abs(normal[2]) > abs(normal[1])) {
524  vec1 = normal.cross(LVector3(0, 1, 0));
525  } else {
526  vec1 = normal.cross(LVector3(0, 0, 1));
527  }
528 
529  // Find a second vector perpendicular to the two.
530  LVector3 vec2 = normal.cross(vec1);
531 
532  EggGroup *egg_plane;
533  if (num_solids == 1) {
534  egg_plane = egg_group;
535  } else {
536  egg_plane = new EggGroup;
537  egg_group->add_child(egg_plane);
538  }
539  egg_plane->set_cs_type(EggGroup::CST_plane);
540  egg_plane->set_collide_flags(flags);
541 
542  EggVertex ev0, ev1, ev2;
543  ev0.set_pos(LCAST(double, origin * net_mat));
544  ev1.set_pos(LCAST(double, (origin + vec1) * net_mat));
545  ev2.set_pos(LCAST(double, (origin + vec2) * net_mat));
546 
547  EggPolygon *egg_poly = new EggPolygon;
548  egg_plane->add_child(egg_poly);
549 
550  egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
551  egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
552  egg_poly->add_vertex(cvpool->create_unique_vertex(ev2));
553 
554  } else if (child->is_of_type(CollisionBox::get_class_type())) {
555  CPT(CollisionBox) box = DCAST(CollisionBox, child);
556  LPoint3 min_point = box->get_min();
557  LPoint3 max_point = box->get_max();
558 
559  EggGroup *egg_box;
560  if (num_solids == 1) {
561  egg_box = egg_group;
562  } else {
563  egg_box = new EggGroup;
564  egg_group->add_child(egg_box);
565  }
566  egg_box->set_cs_type(EggGroup::CST_box);
567  egg_box->set_collide_flags(flags);
568 
569  // Just add the min and max points.
570  EggVertex ev0, ev1;
571  ev0.set_pos(LCAST(double, min_point * net_mat));
572  ev1.set_pos(LCAST(double, max_point * net_mat));
573 
574  EggLine *egg_poly = new EggLine;
575  egg_box->add_child(egg_poly);
576 
577  egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
578  egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
579 
580  } else if (child->is_of_type(CollisionTube::get_class_type())) {
581  CPT(CollisionTube) tube = DCAST(CollisionTube, child);
582  LPoint3 point_a = tube->get_point_a();
583  LPoint3 point_b = tube->get_point_b();
584  LPoint3 centroid = (point_a + point_b) * 0.5f;
585 
586  // Also get an arbitrary vector perpendicular to the tube.
587  LVector3 axis = point_b - point_a;
588  LVector3 sideways;
589  if (abs(axis[2]) > abs(axis[1])) {
590  sideways = axis.cross(LVector3(0, 1, 0));
591  } else {
592  sideways = axis.cross(LVector3(0, 0, 1));
593  }
594  sideways.normalize();
595  sideways *= tube->get_radius();
596  LVector3 extend = axis.normalized() * tube->get_radius();
597 
598  EggGroup *egg_tube;
599  if (num_solids == 1) {
600  egg_tube = egg_group;
601  } else {
602  egg_tube = new EggGroup;
603  egg_group->add_child(egg_tube);
604  }
605  egg_tube->set_cs_type(EggGroup::CST_tube);
606  egg_tube->set_collide_flags(flags);
607 
608  // Add two points for the endcaps, and then two points around the
609  // centroid to indicate the radius.
610  EggVertex ev0, ev1, ev2, ev3;
611  ev0.set_pos(LCAST(double, (point_a - extend) * net_mat));
612  ev1.set_pos(LCAST(double, (centroid + sideways) * net_mat));
613  ev2.set_pos(LCAST(double, (point_b + extend) * net_mat));
614  ev3.set_pos(LCAST(double, (centroid - sideways) * net_mat));
615 
616  EggPolygon *egg_poly = new EggPolygon;
617  egg_tube->add_child(egg_poly);
618 
619  egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
620  egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
621  egg_poly->add_vertex(cvpool->create_unique_vertex(ev2));
622  egg_poly->add_vertex(cvpool->create_unique_vertex(ev3));
623 
624  } else {
625  nout << "Encountered unknown collision solid type " << child->get_type() << "\n";
626  }
627  }
628  }
629 
630  // recurse over children - hm. do I need to do this?
631  recurse_nodes(node_path, egg_group, has_decal);
632 }
633 
634 ////////////////////////////////////////////////////////////////////
635 // Function: EggSaver::convert_geom_node
636 // Access: Private
637 // Description: Converts a GeomNode to the corresponding egg
638 // structures.
639 ////////////////////////////////////////////////////////////////////
640 void EggSaver::
641 convert_geom_node(GeomNode *node, const WorkingNodePath &node_path,
642  EggGroupNode *egg_parent, bool has_decal, CharacterJointMap *jointMap) {
643  PT(EggGroup) egg_group = new EggGroup(node->get_name());
644  bool fancy_attributes = apply_node_properties(egg_group, node);
645 
646  if (node->get_effects()->has_decal()) {
647  has_decal = true;
648  }
649 
650  if (has_decal) {
651  egg_group->set_decal_flag(true);
652  }
653 
654  if (fancy_attributes || has_decal || !node->get_name().empty()) {
655  // If we have any fancy attributes on the node, or if we're making
656  // decal geometry, we have to make a special node to hold the
657  // geometry (normally it would just appear within its parent).
658  egg_parent->add_child(egg_group.p());
659  egg_parent = egg_group;
660  }
661 
662  NodePath np = node_path.get_node_path();
663  CPT(RenderState) net_state = np.get_net_state();
664  CPT(TransformState) net_transform = np.get_net_transform();
665  LMatrix4 net_mat = net_transform->get_mat();
666  LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv());
667  net_mat = net_mat * inv;
668 
669  // Now get out all the various kinds of geometry.
670  int num_geoms = node->get_num_geoms();
671  for (int i = 0; i < num_geoms; ++i) {
672  CPT(RenderState) geom_state = net_state->compose(node->get_geom_state(i));
673 
674  const Geom *geom = node->get_geom(i);
675  int num_primitives = geom->get_num_primitives();
676  for (int j = 0; j < num_primitives; ++j) {
677  const GeomPrimitive *primitive = geom->get_primitive(j);
678  CPT(GeomPrimitive) simple = primitive->decompose();
679  CPT(GeomVertexData) vdata = geom->get_vertex_data();
680  // vdata = vdata->animate_vertices(true, Thread::get_current_thread());
681  convert_primitive(vdata, simple, geom_state,
682  net_mat, egg_parent, jointMap);
683  }
684  }
685 
686  recurse_nodes(node_path, egg_parent, has_decal);
687 }
688 
689 ////////////////////////////////////////////////////////////////////
690 // Function: EggSaver::convert_primitive
691 // Access: Private
692 // Description:
693 ////////////////////////////////////////////////////////////////////
694 void EggSaver::
695 convert_primitive(const GeomVertexData *vertex_data,
696  const GeomPrimitive *primitive,
697  const RenderState *net_state,
698  const LMatrix4 &net_mat, EggGroupNode *egg_parent,
699  CharacterJointMap *jointMap) {
700  GeomVertexReader reader(vertex_data);
701 
702  // Check for a color scale.
703  LVecBase4 color_scale(1.0f, 1.0f, 1.0f, 1.0f);
704  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, net_state->get_attrib(ColorScaleAttrib::get_class_type()));
705  if (csa != (const ColorScaleAttrib *)NULL) {
706  color_scale = csa->get_scale();
707  }
708 
709  // Check for a color override.
710  bool has_color_override = false;
711  bool has_color_off = false;
712  LColor color_override;
713  const ColorAttrib *ca = DCAST(ColorAttrib, net_state->get_attrib(ColorAttrib::get_class_type()));
714  if (ca != (const ColorAttrib *)NULL) {
715  if (ca->get_color_type() == ColorAttrib::T_flat) {
716  has_color_override = true;
717  color_override = ca->get_color();
718  color_override.set(color_override[0] * color_scale[0],
719  color_override[1] * color_scale[1],
720  color_override[2] * color_scale[2],
721  color_override[3] * color_scale[3]);
722 
723  } else if (ca->get_color_type() == ColorAttrib::T_off) {
724  has_color_off = true;
725  }
726  }
727 
728  // Check for a material.
729  EggMaterial *egg_mat = (EggMaterial *)NULL;
730  const MaterialAttrib *ma = DCAST(MaterialAttrib, net_state->get_attrib(MaterialAttrib::get_class_type()));
731  if (ma != (const MaterialAttrib *)NULL) {
732  egg_mat = get_egg_material(ma->get_material());
733  }
734 
735  // Check for a texture.
736  EggTexture *egg_tex = (EggTexture *)NULL;
737  const TextureAttrib *ta = DCAST(TextureAttrib, net_state->get_attrib(TextureAttrib::get_class_type()));
738  if (ta != (const TextureAttrib *)NULL) {
739  egg_tex = get_egg_texture(ta->get_texture());
740  }
741 
742  // Check the texture environment
743  if ((ta != (const TextureAttrib *)NULL) && (egg_tex != (const EggTexture *)NULL)) {
744  TextureStage* tex_stage = ta->get_on_stage(0);
745  if (tex_stage != (const TextureStage *)NULL) {
746  switch (tex_stage->get_mode()) {
747  case TextureStage::M_modulate:
748  if (has_color_off == true) {
749  egg_tex->set_env_type(EggTexture::ET_replace);
750  } else {
751  egg_tex->set_env_type(EggTexture::ET_modulate);
752  }
753  break;
754  case TextureStage::M_decal:
755  egg_tex->set_env_type(EggTexture::ET_decal);
756  break;
757  case TextureStage::M_blend:
758  egg_tex->set_env_type(EggTexture::ET_blend);
759  break;
760  case TextureStage::M_replace:
761  egg_tex->set_env_type(EggTexture::ET_replace);
762  break;
763  case TextureStage::M_add:
764  egg_tex->set_env_type(EggTexture::ET_add);
765  break;
766  case TextureStage::M_blend_color_scale:
767  egg_tex->set_env_type(EggTexture::ET_blend_color_scale);
768  break;
769  default:
770  break;
771  }
772  }
773  }
774 
775  // Check the backface flag.
776  bool bface = false;
777  const RenderAttrib *cf_attrib = net_state->get_attrib(CullFaceAttrib::get_class_type());
778  if (cf_attrib != (const RenderAttrib *)NULL) {
779  const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, cf_attrib);
780  if (cfa->get_effective_mode() == CullFaceAttrib::M_cull_none) {
781  bface = true;
782  }
783  }
784 
785  // Check the depth write flag - only needed for AM_blend_no_occlude
786  bool has_depthwrite = false;
787  DepthWriteAttrib::Mode depthwrite = DepthWriteAttrib::M_on;
788  const RenderAttrib *dw_attrib = net_state->get_attrib(DepthWriteAttrib::get_class_type());
789  if (dw_attrib != (const RenderAttrib *)NULL) {
790  const DepthWriteAttrib *dwa = DCAST(DepthWriteAttrib, dw_attrib);
791  depthwrite = dwa->get_mode();
792  has_depthwrite = true;
793  }
794 
795  // Check the transparency flag.
796  bool has_transparency = false;
797  TransparencyAttrib::Mode transparency = TransparencyAttrib::M_none;
798  const RenderAttrib *tr_attrib = net_state->get_attrib(TransparencyAttrib::get_class_type());
799  if (tr_attrib != (const RenderAttrib *)NULL) {
800  const TransparencyAttrib *tra = DCAST(TransparencyAttrib, tr_attrib);
801  transparency = tra->get_mode();
802  has_transparency = true;
803  }
804  if (has_transparency && (egg_tex != (EggTexture *)NULL)) {
805  EggRenderMode::AlphaMode tex_trans = EggRenderMode::AM_unspecified;
806  switch (transparency) {
807  case TransparencyAttrib::M_none:
808  tex_trans = EggRenderMode::AM_off;
809  break;
810  case TransparencyAttrib::M_alpha:
811  if (has_depthwrite && (depthwrite == DepthWriteAttrib::M_off)) {
812  tex_trans = EggRenderMode::AM_blend_no_occlude;
813  has_depthwrite = false;
814  } else {
815  tex_trans = EggRenderMode::AM_blend;
816  }
817  break;
818  case TransparencyAttrib::M_multisample:
819  tex_trans = EggRenderMode::AM_ms;
820  break;
821  case TransparencyAttrib::M_multisample_mask:
822  tex_trans = EggRenderMode::AM_ms_mask;
823  break;
824  case TransparencyAttrib::M_binary:
825  tex_trans = EggRenderMode::AM_binary;
826  break;
827  case TransparencyAttrib::M_dual:
828  tex_trans = EggRenderMode::AM_dual;
829  break;
830  default: // intentional fall-through
831  case TransparencyAttrib::M_notused:
832  break;
833  }
834  if (tex_trans != EggRenderMode::AM_unspecified) {
835  egg_tex->set_alpha_mode(tex_trans);
836  }
837  }
838 
839 
840  LNormal normal;
841  LColor color;
842  CPT(TransformBlendTable) transformBlendTable = vertex_data->get_transform_blend_table();
843 
844  int num_primitives = primitive->get_num_primitives();
845  int num_vertices = primitive->get_num_vertices_per_primitive();
846 
847  EggPrimitive *(*make_func)(void);
848 
849  if (primitive->is_of_type(GeomTriangles::get_class_type())) {
850  make_func = make_egg_polygon;
851  } else if (primitive->is_of_type(GeomPatches::get_class_type())) {
852  make_func = make_egg_patch;
853  } else if (primitive->is_of_type(GeomPoints::get_class_type())) {
854  make_func = make_egg_point;
855  } else if (primitive->is_of_type(GeomLines::get_class_type())) {
856  make_func = make_egg_line;
857  } else {
858  // Huh, an unknown geometry type.
859  return;
860  }
861 
862  for (int i = 0; i < num_primitives; ++i) {
863  PT(EggPrimitive) egg_prim = (*make_func)();
864 
865  egg_parent->add_child(egg_prim);
866 
867  if (egg_mat != (EggMaterial *)NULL) {
868  egg_prim->set_material(egg_mat);
869  }
870  if (egg_tex != (EggTexture *)NULL) {
871  egg_prim->set_texture(egg_tex);
872  }
873 
874  if (bface) {
875  egg_prim->set_bface_flag(true);
876  }
877 
878  for (int j = 0; j < num_vertices; j++) {
879  EggVertex egg_vert;
880 
881  // Get per-vertex properties.
882  reader.set_row(primitive->get_vertex(i * num_vertices + j));
883 
884  reader.set_column(InternalName::get_vertex());
885  LVertex vertex = reader.get_data3();
886  egg_vert.set_pos(LCAST(double, vertex * net_mat));
887 
888  if (vertex_data->has_column(InternalName::get_normal())) {
889  reader.set_column(InternalName::get_normal());
890  LNormal normal = reader.get_data3();
891  egg_vert.set_normal(LCAST(double, normal * net_mat));
892  }
893  if (has_color_override) {
894  egg_vert.set_color(color_override);
895 
896  } else if (!has_color_off) {
897  LColor color(1.0f, 1.0f, 1.0f, 1.0f);
898  if (vertex_data->has_column(InternalName::get_color())) {
899  reader.set_column(InternalName::get_color());
900  color = reader.get_data4();
901  }
902  egg_vert.set_color(LColor(color[0] * color_scale[0],
903  color[1] * color_scale[1],
904  color[2] * color_scale[2],
905  color[3] * color_scale[3]));
906  }
907 
908  if (vertex_data->has_column(InternalName::get_texcoord())) {
909  reader.set_column(InternalName::get_texcoord());
910  LTexCoord uv = reader.get_data2();
911  egg_vert.set_uv(LCAST(double, uv));
912  }
913 
914  EggVertex *new_egg_vert = _vpool->create_unique_vertex(egg_vert);
915 
916  if ((vertex_data->has_column(InternalName::get_transform_blend())) &&
917  (jointMap!=NULL) && (transformBlendTable!=NULL)) {
918  reader.set_column(InternalName::get_transform_blend());
919  int idx = reader.get_data1i();
920  const TransformBlend &blend = transformBlendTable->get_blend(idx);
921  int num_weights = blend.get_num_transforms();
922  for (int k = 0; k < num_weights; ++k) {
923  PN_stdfloat weight = blend.get_weight(k);
924  if (weight!=0) {
925  const VertexTransform *vertex_transform = blend.get_transform(k);
926  if (vertex_transform->is_of_type(JointVertexTransform::get_class_type())) {
927  const JointVertexTransform *joint_vertex_transform = DCAST(const JointVertexTransform, vertex_transform);
928 
929  CharacterJointMap::iterator mi = jointMap->find(joint_vertex_transform->get_joint());
930  if (mi == jointMap->end()) {
931  mi = jointMap->insert(CharacterJointMap::value_type(joint_vertex_transform->get_joint(), pvector<pair<EggVertex*,PN_stdfloat> >())).first;
932  }
933  pvector<pair<EggVertex*,PN_stdfloat> > &joint_vertices = (*mi).second;
934  joint_vertices.push_back(pair<EggVertex*,PN_stdfloat>(new_egg_vert, weight));
935  }
936  }
937  }
938  }
939 
940  egg_prim->add_vertex(new_egg_vert);
941  }
942  }
943 }
944 
945 ////////////////////////////////////////////////////////////////////
946 // Function: EggSaver::recurse_nodes
947 // Access: Private
948 // Description: Converts all the children of the indicated node.
949 ////////////////////////////////////////////////////////////////////
950 void EggSaver::
951 recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent,
952  bool has_decal) {
953  PandaNode *node = node_path.node();
954  int num_children = node->get_num_children();
955 
956  for (int i = 0; i < num_children; i++) {
957  PandaNode *child = node->get_child(i);
958  convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal);
959  }
960 }
961 
962 ////////////////////////////////////////////////////////////////////
963 // Function: EggSaver::apply_node_properties
964 // Access: Private
965 // Description: Applies any special properties that might be stored
966 // on the node, like billboarding. Returns true if any
967 // were applied, false otherwise.
968 ////////////////////////////////////////////////////////////////////
969 bool EggSaver::
970 apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage) {
971  bool any_applied = false;
972 
973  if (node->is_overall_hidden() && allow_backstage) {
974  // This node is hidden. We'll go ahead and convert it, but we'll
975  // put in the "backstage" flag to mean it's not real geometry.
976  // unless the caller wants to keep it (by setting allow_backstage to false)
977  egg_group->add_object_type("backstage");
978  }
979 
980  if (node->has_tags()) {
981  if (apply_tags(egg_group, node)) {
982  any_applied = true;
983  }
984  }
985 
986  if (node->is_of_type(ModelNode::get_class_type())) {
987  ModelNode *model_node = DCAST(ModelNode, node);
988  switch (model_node->get_preserve_transform()) {
989  case ModelNode::PT_none:
990  case ModelNode::PT_drop_node:
991  break;
992 
993  case ModelNode::PT_net:
994  egg_group->set_dcs_type(EggGroup::DC_net);
995  break;
996 
997  case ModelNode::PT_local:
998  egg_group->set_dcs_type(EggGroup::DC_local);
999  break;
1000 
1001  case ModelNode::PT_no_touch:
1002  egg_group->set_dcs_type(EggGroup::DC_no_touch);
1003  break;
1004  }
1005  }
1006 
1007  const RenderEffects *effects = node->get_effects();
1008  const RenderEffect *effect = effects->get_effect(BillboardEffect::get_class_type());
1009  if (effect != (RenderEffect *)NULL) {
1010  const BillboardEffect *bbe = DCAST(BillboardEffect, effect);
1011  if (bbe->get_axial_rotate()) {
1012  egg_group->set_billboard_type(EggGroup::BT_axis);
1013  any_applied = true;
1014 
1015  } else if (bbe->get_eye_relative()) {
1016  egg_group->set_billboard_type(EggGroup::BT_point_camera_relative);
1017  any_applied = true;
1018 
1019  } else {
1020  egg_group->set_billboard_type(EggGroup::BT_point_world_relative);
1021  any_applied = true;
1022  }
1023  }
1024 
1025  const TransformState *transform = node->get_transform();
1026  if (!transform->is_identity()) {
1027  if (transform->has_components()) {
1028  // If the transform can be represented componentwise, we prefer
1029  // storing it that way in the egg file.
1030  const LVecBase3 &scale = transform->get_scale();
1031  const LQuaternion &quat = transform->get_quat();
1032  const LVecBase3 &pos = transform->get_pos();
1033  if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) {
1034  egg_group->add_scale3d(LCAST(double, scale));
1035  }
1036  if (!quat.is_identity()) {
1037  egg_group->add_rotate3d(LCAST(double, quat));
1038  }
1039  if (!pos.almost_equal(LVecBase3::zero())) {
1040  egg_group->add_translate3d(LCAST(double, pos));
1041  }
1042 
1043  } else if (transform->has_mat()) {
1044  // Otherwise, we store the raw matrix.
1045  const LMatrix4 &mat = transform->get_mat();
1046  egg_group->set_transform3d(LCAST(double, mat));
1047  }
1048  any_applied = true;
1049  }
1050 
1051  return any_applied;
1052 }
1053 
1054 ////////////////////////////////////////////////////////////////////
1055 // Function: EggSaver::apply_tags
1056 // Access: Private
1057 // Description: Applies string tags to the egg file. Returns true if
1058 // any were applied, false otherwise.
1059 ////////////////////////////////////////////////////////////////////
1060 bool EggSaver::
1061 apply_tags(EggGroup *egg_group, PandaNode *node) {
1062  ostringstream strm;
1063  char delimiter = '\n';
1064  string delimiter_str(1, delimiter);
1065  node->list_tags(strm, delimiter_str);
1066 
1067  string data = strm.str();
1068  if (data.empty()) {
1069  return false;
1070  }
1071 
1072  bool any_applied = false;
1073 
1074  size_t p = 0;
1075  size_t q = data.find(delimiter);
1076  while (q != string::npos) {
1077  string tag = data.substr(p, q);
1078  if (apply_tag(egg_group, node, tag)) {
1079  any_applied = true;
1080  }
1081  p = q + 1;
1082  q = data.find(delimiter, p);
1083  }
1084 
1085  string tag = data.substr(p);
1086  if (apply_tag(egg_group, node, tag)) {
1087  any_applied = true;
1088  }
1089 
1090  return any_applied;
1091 }
1092 
1093 ////////////////////////////////////////////////////////////////////
1094 // Function: EggSaver::apply_tag
1095 // Access: Private
1096 // Description: Applies the named string tags to the egg file.
1097 ////////////////////////////////////////////////////////////////////
1098 bool EggSaver::
1099 apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag) {
1100  if (!node->has_tag(tag)) {
1101  return false;
1102  }
1103 
1104  string value = node->get_tag(tag);
1105  egg_group->set_tag(tag, value);
1106  return true;
1107 }
1108 
1109 ////////////////////////////////////////////////////////////////////
1110 // Function: EggSaver::get_egg_material
1111 // Access: Private
1112 // Description: Returns an EggMaterial pointer that corresponds to
1113 // the indicated Material.
1114 ////////////////////////////////////////////////////////////////////
1115 EggMaterial *EggSaver::
1116 get_egg_material(Material *mat) {
1117  if (mat != (Material *)NULL) {
1118  EggMaterial temp(mat->get_name());
1119  if (mat->has_ambient()) {
1120  temp.set_amb(mat->get_ambient());
1121  }
1122 
1123  if (mat->has_diffuse()) {
1124  temp.set_diff(mat->get_diffuse());
1125  }
1126 
1127  if (mat->has_specular()) {
1128  temp.set_spec(mat->get_specular());
1129  }
1130 
1131  if (mat->has_emission()) {
1132  temp.set_emit(mat->get_emission());
1133  }
1134 
1135  temp.set_shininess(mat->get_shininess());
1136  temp.set_local(mat->get_local());
1137 
1138  return _materials.create_unique_material(temp, ~EggMaterial::E_mref_name);
1139  }
1140 
1141  return NULL;
1142 }
1143 
1144 ////////////////////////////////////////////////////////////////////
1145 // Function: EggSaver::get_egg_texture
1146 // Access: Private
1147 // Description: Returns an EggTexture pointer that corresponds to the
1148 // indicated Texture.
1149 ////////////////////////////////////////////////////////////////////
1150 EggTexture *EggSaver::
1151 get_egg_texture(Texture *tex) {
1152  if (tex != (Texture *)NULL) {
1153  if (tex->has_filename()) {
1154  Filename filename = tex->get_filename();
1155  EggTexture temp(filename.get_basename_wo_extension(), filename);
1156  if (tex->has_alpha_filename()) {
1157  Filename alpha = tex->get_alpha_filename();
1158  temp.set_alpha_filename(alpha);
1159  }
1160 
1161  switch (tex->get_minfilter()) {
1162  case SamplerState::FT_nearest:
1163  temp.set_minfilter(EggTexture::FT_nearest);
1164  break;
1165  case SamplerState::FT_linear:
1166  temp.set_minfilter(EggTexture::FT_linear);
1167  break;
1168  case SamplerState::FT_nearest_mipmap_nearest:
1169  temp.set_minfilter(EggTexture::FT_nearest_mipmap_nearest);
1170  break;
1171  case SamplerState::FT_linear_mipmap_nearest:
1172  temp.set_minfilter(EggTexture::FT_linear_mipmap_nearest);
1173  break;
1174  case SamplerState::FT_nearest_mipmap_linear:
1175  temp.set_minfilter(EggTexture::FT_nearest_mipmap_linear);
1176  break;
1177  case SamplerState::FT_linear_mipmap_linear:
1178  temp.set_minfilter(EggTexture::FT_linear_mipmap_linear);
1179  break;
1180 
1181  default:
1182  break;
1183  }
1184 
1185  switch (tex->get_magfilter()) {
1186  case SamplerState::FT_nearest:
1187  temp.set_magfilter(EggTexture::FT_nearest);
1188  break;
1189  case SamplerState::FT_linear:
1190  temp.set_magfilter(EggTexture::FT_linear);
1191  break;
1192 
1193  default:
1194  break;
1195  }
1196 
1197  switch (tex->get_wrap_u()) {
1198  case SamplerState::WM_clamp:
1199  temp.set_wrap_u(EggTexture::WM_clamp);
1200  break;
1201  case SamplerState::WM_repeat:
1202  temp.set_wrap_u(EggTexture::WM_repeat);
1203  break;
1204 
1205  default:
1206  // There are some new wrap options on Texture that aren't yet
1207  // supported in egg.
1208  break;
1209  }
1210 
1211  switch (tex->get_wrap_v()) {
1212  case SamplerState::WM_clamp:
1213  temp.set_wrap_v(EggTexture::WM_clamp);
1214  break;
1215  case SamplerState::WM_repeat:
1216  temp.set_wrap_v(EggTexture::WM_repeat);
1217  break;
1218 
1219  default:
1220  // There are some new wrap options on Texture that aren't yet
1221  // supported in egg.
1222  break;
1223  }
1224 
1225  switch (tex->get_format()) {
1226  case Texture::F_red:
1227  temp.set_format(EggTexture::F_red);
1228  break;
1229  case Texture::F_green:
1230  temp.set_format(EggTexture::F_green);
1231  break;
1232  case Texture::F_blue:
1233  temp.set_format(EggTexture::F_blue);
1234  break;
1235  case Texture::F_alpha:
1236  temp.set_format(EggTexture::F_alpha);
1237  break;
1238  case Texture::F_rgb:
1239  temp.set_format(EggTexture::F_rgb);
1240  break;
1241  case Texture::F_rgb5:
1242  temp.set_format(EggTexture::F_rgb5);
1243  break;
1244  case Texture::F_rgb8:
1245  temp.set_format(EggTexture::F_rgb8);
1246  break;
1247  case Texture::F_rgb12:
1248  temp.set_format(EggTexture::F_rgb12);
1249  break;
1250  case Texture::F_rgb332:
1251  temp.set_format(EggTexture::F_rgb332);
1252  break;
1253  case Texture::F_rgba:
1254  temp.set_format(EggTexture::F_rgba);
1255  break;
1256  case Texture::F_rgbm:
1257  temp.set_format(EggTexture::F_rgbm);
1258  break;
1259  case Texture::F_rgba4:
1260  temp.set_format(EggTexture::F_rgba4);
1261  break;
1262  case Texture::F_rgba5:
1263  temp.set_format(EggTexture::F_rgba5);
1264  break;
1265  case Texture::F_rgba8:
1266  temp.set_format(EggTexture::F_rgba8);
1267  break;
1268  case Texture::F_rgba12:
1269  temp.set_format(EggTexture::F_rgba12);
1270  break;
1271  case Texture::F_luminance:
1272  temp.set_format(EggTexture::F_luminance);
1273  break;
1274  case Texture::F_luminance_alpha:
1275  temp.set_format(EggTexture::F_luminance_alpha);
1276  break;
1277  case Texture::F_luminance_alphamask:
1278  temp.set_format(EggTexture::F_luminance_alphamask);
1279  break;
1280  default:
1281  break;
1282  }
1283 
1284  return _textures.create_unique_texture(temp, ~EggTexture::E_tref_name);
1285  }
1286  }
1287 
1288  return NULL;
1289 }
1290 
1291 ////////////////////////////////////////////////////////////////////
1292 // Function: EggSaver::make_egg_polygon
1293 // Access: Private, Static
1294 // Description: A factory function to make a new EggPolygon instance.
1295 ////////////////////////////////////////////////////////////////////
1296 EggPrimitive *EggSaver::
1297 make_egg_polygon() {
1298  return new EggPolygon;
1299 }
1300 
1301 ////////////////////////////////////////////////////////////////////
1302 // Function: EggSaver::make_egg_patch
1303 // Access: Private, Static
1304 // Description: A factory function to make a new EggPatch instance.
1305 ////////////////////////////////////////////////////////////////////
1306 EggPrimitive *EggSaver::
1307 make_egg_patch() {
1308  return new EggPatch;
1309 }
1310 
1311 ////////////////////////////////////////////////////////////////////
1312 // Function: EggSaver::make_egg_point
1313 // Access: Private, Static
1314 // Description: A factory function to make a new EggPoint instance.
1315 ////////////////////////////////////////////////////////////////////
1316 EggPrimitive *EggSaver::
1317 make_egg_point() {
1318  return new EggPoint;
1319 }
1320 
1321 ////////////////////////////////////////////////////////////////////
1322 // Function: EggSaver::make_egg_line
1323 // Access: Private, Static
1324 // Description: A factory function to make a new EggLine instance.
1325 ////////////////////////////////////////////////////////////////////
1326 EggPrimitive *EggSaver::
1327 make_egg_line() {
1328  return new EggLine;
1329 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights...
Definition: eggPrimitive.h:51
double get_frame_rate() const
Returns the native frame rate of the animation.
const RenderState * get_geom_state(int n) const
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.I:106
Format get_format() const
Returns the format of the texture, which represents both the semantic meaning of the texels and...
Definition: texture.I:872
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
const CharacterJoint * get_joint() const
Returns the joint for which this object returns the transform.
PandaNode * node() const
Returns the node traversed to so far.
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
bool get_local() const
Returns the local viewer flag.
Definition: material.I:242
void add_rotate3d(double angle, const LVector3d &axis)
Appends a 3-d rotation about an arbitrary axis to the current transform.
const Filename & get_alpha_filename() const
Returns the alpha_filename that has been set.
Definition: texture.I:575
Material * get_material() const
If the MaterialAttrib is not an &#39;off&#39; MaterialAttrib, returns the material that is associated...
EggTexture * create_unique_texture(const EggTexture &copy, int eq)
Creates a new texture if there is not already one equivalent (according to eq, see EggTexture::is_equ...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
A line segment, or a series of connected line segments, defined by a <Line> entry.
Definition: eggLine.h:27
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:60
const LColor & get_color() const
If the type is T_flat or T_off, this returns the color that will be applied to geometry.
Definition: colorAttrib.I:58
bool get_axial_rotate() const
Returns true if this billboard rotates only around the axis of the up vector, or false if it rotates ...
bool set_column(int column)
Sets up the reader to use the nth data type of the GeomVertexFormat, numbering from 0...
An animated character, with skeleton-morph animation and either soft-skinned or hard-skinned vertices...
Definition: character.h:41
int get_num_children() const
Returns the number of child nodes of the group.
Definition: animGroup.cxx:104
Indicates which, if any, material should be applied to geometry.
SamplerState::WrapMode get_wrap_v() const
Returns the wrap mode of the texture in the V direction.
Definition: texture.I:1103
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of child nodes this node has.
Definition: pandaNode.I:68
const LVecBase4 & get_scale() const
Returns the scale to be applied to colors.
void set_pos(double pos)
Sets the vertex position.
Definition: eggVertex.I:54
bool has_ambient() const
Returns true if the ambient color has been explicitly set for this material, false otherwise...
Definition: material.I:70
Enables or disables writing to the depth buffer.
void add_component_data(const string &component_name, double value)
Adds a new row to the named component (one of matrix_component_letters) of the table.
This is the root of an AnimChannel hierarchy.
Definition: animBundle.h:31
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
The abstract base class for all things that can collide with other things in the world, and all the things they can collide with (except geometry).
PN_stdfloat get_shininess() const
Returns the shininess exponent of the material.
Definition: material.I:217
This controls the enabling of transparency.
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:33
const LVecBase2 & get_data2()
Returns the data associated with the read row, expressed as a 2-component value, and advances the rea...
Mode get_mode() const
Returns the transparency mode.
A cuboid collision volume or object.
Definition: collisionBox.h:30
double get_base_frame_rate() const
Returns the ideal number of frames per second of the animation, when it is running at normal speed...
Definition: animBundle.I:47
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
PandaNode * get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the nth child node of this node.
Definition: pandaNode.I:82
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:63
SamplerState::FilterType get_magfilter() const
Returns the filter mode of the texture for magnification.
Definition: texture.I:1157
bool has_alpha_filename() const
Returns true if the alpha_filename has been set and is available.
Definition: texture.I:561
int get_num_children() const
Returns the number of child nodes of the group.
Definition: partGroup.cxx:111
bool has_table(char table_id) const
Returns true if the indicated subtable has been assigned.
PN_stdfloat get_out(int index) const
Returns the "out" distance of the indicated switch range.
Definition: lodNode.I:168
const LVecBase4 & get_data4()
Returns the data associated with the read row, expressed as a 4-component value, and advances the rea...
A node that automatically cycles through rendering each one of its children according to its frame ra...
Definition: sequenceNode.h:29
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is the base class for a number of special render effects that may be set on scene graph nodes to...
Definition: renderEffect.h:56
PreserveTransform get_preserve_transform() const
Returns the current setting of the preserve_transform flag.
Definition: modelNode.I:76
Mode get_mode() const
Return the mode of this stage.
Definition: textureStage.I:206
A spherical collision volume or object.
int get_num_geoms() const
Returns the number of geoms in the node.
Definition: geomNode.I:46
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
const LPoint3 & get_center() const
Returns the center of the LOD.
Definition: lodNode.I:254
Indicates that geometry at this node should automatically rotate to face the camera, or any other arbitrary node.
static const LVecBase3f & zero()
Returns a zero-length vector.
Definition: lvecBase3.h:382
This is a class designed to support low-overhead traversals of the complete scene graph...
void get_blend(LMatrix4 &result, Thread *current_thread) const
Returns the current value of the blend, based on the current value of all of the nested transform obj...
Type get_color_type() const
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.I:46
bool is_overall_hidden() const
Returns true if the node has been hidden to all cameras by clearing its overall bit.
Definition: pandaNode.I:524
A single point, or a collection of points as defined by a single <PointLight> entry.
Definition: eggPoint.h:27
bool almost_equal(const LVecBase3f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase3.h:1280
Mode get_mode() const
Returns the depth write mode.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:34
Indicates which faces should be culled based on their vertex ordering.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
const LColor & get_diffuse() const
Returns the diffuse color setting, if it has been set.
Definition: material.I:119
SamplerState::WrapMode get_wrap_u() const
Returns the wrap mode of the texture in the U direction.
Definition: texture.I:1087
const LColor & get_ambient() const
Returns the ambient color setting, if it has been set.
Definition: material.I:82
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:36
PartGroup * get_child(int n) const
Returns the nth child of the group.
Definition: partGroup.cxx:122
void set_transform3d(const LMatrix4d &mat)
Sets the overall transform as a 4x4 matrix.
Definition: eggTransform.I:222
bool has_filename() const
Returns true if the filename has been set and is available.
Definition: texture.I:536
This is the base class for AnimChannel and AnimBundle.
Definition: animGroup.h:36
This node is placed at key points within the scene graph to indicate the roots of "models": subtrees ...
Definition: modelNode.h:34
NodePath get_node_path() const
Constructs and returns an actual NodePath that represents the same path we have just traversed...
PN_stdfloat get_weight(const VertexTransform *transform) const
Returns the weight associated with the indicated transform, or 0 if there is no entry for the transfo...
AnimGroup * get_child(int n) const
Returns the nth child of the group.
Definition: animGroup.cxx:115
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This is an abstract base class that holds a pointer to some transform, computed in some arbitrary way...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
A single "patch", a special primitive to be rendered only with a tessellation shader.
Definition: eggPatch.h:27
A Level-of-Detail node.
Definition: lodNode.h:31
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
const VertexTransform * get_transform(int n) const
Returns the nth transform stored in the blend object.
This corresponds to an <Xfm$Anim_S$> entry, which is a collection of up to nine <S$Anim> entries that...
Definition: eggXfmSAnim.h:33
void set_vertex_membership(EggVertex *vert, double membership)
Explicitly sets the net membership of the indicated vertex in this group to the given value...
Definition: eggGroup.cxx:773
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
PN_stdfloat get_in(int index) const
Returns the "in" distance of the indicated switch range.
Definition: lodNode.I:154
int get_num_switches() const
Returns the number of switch ranges added to the LODNode.
Definition: lodNode.I:117
A container for geometry primitives.
Definition: geom.h:58
const LVecBase3 & get_data3()
Returns the data associated with the read row, expressed as a 3-component value, and advances the rea...
Texture * get_texture() const
If the TextureAttrib is not an &#39;off&#39; TextureAttrib, returns the base-level texture that is associated...
Definition: textureAttrib.I:76
bool has_specular() const
Returns true if the specular color has been explicitly set for this material, false otherwise...
Definition: material.I:144
TextureStage * get_on_stage(int n) const
Returns the nth stage turned on by the attribute, sorted in render order.
void set_row(int row)
Sets the start row to the indicated value.
int get_num_transforms() const
Returns the number of transforms stored in the blend object.
LVector3f normalized() const
Normalizes the vector and returns the normalized vector as a copy.
Definition: lvector3.h:407
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
A single polygon.
Definition: eggPolygon.h:26
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
Defines the way an object appears in the presence of lighting.
Definition: material.h:34
void add_translate3d(const LVector3d &translate)
Appends a 3-d translation operation to the current transform.
Applies a scale to colors in the scene graph and on vertices.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
This defines a single entry in a TransformBlendTable.
void set_tag(const string &key, const string &value)
Associates a user-defined value with a user-defined key which is stored on the node.
Definition: eggGroup.I:883
This corresponds to a.
Definition: eggTable.h:31
This is a specialization on VertexTransform that returns the transform necessary to move vertices as ...
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
bool is_identity() const
Returns true if this quaternion represents the identity transformation: no rotation.
Definition: lquaternion.h:829
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
int get_num_primitives() const
Returns the number of individual primitives stored within this object.
bool get_eye_relative() const
Returns true if this billboard interprets the up vector relative to the camera, or false if it is rel...
int get_vertex(int i) const
Returns the ith vertex index in the table.
bool has_column(const InternalName *name) const
Returns true if the data has the named column, false otherwise.
This is the base quaternion class.
Definition: lquaternion.h:96
This is the root of a MovingPart hierarchy.
Definition: partBundle.h:49
const RenderEffect * get_effect(int n) const
Returns the nth effect in the state.
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
This represents one joint of the character&#39;s animation, containing an animating transform matrix...
A node in the scene graph that can hold any number of CollisionSolids.
Definition: collisionNode.h:33
void add_scale3d(const LVecBase3d &scale)
Appends a possibly non-uniform scale to the current transform.
EggMaterial * create_unique_material(const EggMaterial &copy, int eq)
Creates a new material if there is not already one equivalent (according to eq, see EggMaterial::is_e...
This structure collects together the different combinations of transforms and blend amounts used by a...
An animation channel that issues a matrix each frame, read from a table such as might have been read ...
void get_transform(LMatrix4 &transform) const
Copies the joint&#39;s current transform into the indicated matrix.
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:30
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
virtual int get_num_vertices_per_primitive() const
If the primitive type is a simple type in which all primitives have the same number of vertices...
Mode get_effective_mode() const
Returns the effective culling mode.
This is a two-component point in space.
Definition: lpoint2.h:92
const LColor & get_emission() const
Returns the emission color setting, if it has been set.
Definition: material.I:193
bool has_diffuse() const
Returns true if the diffuse color has been explicitly set for this material, false otherwise...
Definition: material.I:107
bool has_emission() const
Returns true if the emission color has been explicitly set for this material, false otherwise...
Definition: material.I:181
This is a node that contains a pointer to an AnimBundle.
This implements a solid roughly in cylindrical shape.
Definition: collisionTube.h:30
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2486
CPTA_stdfloat get_table(char table_id) const
Returns a pointer to the indicated subtable&#39;s data, if it exists, or NULL if it does not...
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:38
void add_matrix4(const LMatrix4d &mat)
Appends an arbitrary 4x4 matrix to the current transform.
Definition: eggTransform.I:149
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition: eggVertex.I:239
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
void add_node(PandaNode *node)
Adds the scene graph rooted at the indicated node to the accumulated egg data within this object...
Definition: eggSaver.cxx:96
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
Definition: lvector3.h:541
A collection of vertices.
Definition: eggVertexPool.h:46
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive&#39;s list of vertices, and returns it...
This represents a unique collection of RenderEffect objects that correspond to a particular renderabl...
Definition: renderEffects.h:46
const LColor & get_specular() const
Returns the specular color setting, if it has been set.
Definition: material.I:156
A SwitchCondition that switches the levels-of-detail based on distance from the camera&#39;s eyepoint...
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
SamplerState::FilterType get_minfilter() const
Returns the filter mode of the texture for minification.
Definition: texture.I:1139
A node that renders only one of its children, according to the user&#39;s indication. ...
Definition: switchNode.h:27
Similar to PointerToArray, except that its contents may not be modified.
const LMatrix4d & get_vertex_frame_inv() const
Returns the inverse of the matrix returned by get_vertex_frame().
Definition: eggNode.I:167
const Filename & get_filename() const
Returns the filename that has been set.
Definition: texture.I:549
void list_tags(ostream &out, const string &separator="\) const
Writes a list of all the tag keys assigned to the node to the indicated stream.
Definition: pandaNode.cxx:1505
This is the base class for PartRoot and MovingPart.
Definition: partGroup.h:45