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