Panda3D
Loading...
Searching...
No Matches
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
77using std::pair;
78using std::string;
79
80/**
81 *
82 */
83EggSaver::
84EggSaver(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 */
97add_node(PandaNode *node) {
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 */
117add_subgraph(PandaNode *root) {
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 */
135void EggSaver::
136convert_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 */
173void EggSaver::
174convert_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 */
219void EggSaver::
220convert_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 */
250void EggSaver::
251convert_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 */
280EggGroupNode * 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 */
325void EggSaver::
326convert_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 */
346void EggSaver::
347convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, CharacterJointMap *joint_map) {
348 convert_character_bundle(bundleNode, egg_parent, joint_map, nullptr);
349}
350
351/**
352 * Converts the indicated Character Bundle to the corresponding Egg joints
353 * structure.
354 */
355void EggSaver::
356convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent,
357 CharacterJointMap *joint_map, const CharacterJoint *parent_joint) {
358 int num_children = bundleNode->get_num_children();
359
360 const CharacterJoint *character_joint = nullptr;
361
362 EggGroupNode *joint_group = egg_parent;
363 if (bundleNode->is_of_type(CharacterJoint::get_class_type())) {
364 character_joint = DCAST(CharacterJoint, bundleNode);
365
366 EggGroup *joint = new EggGroup(bundleNode->get_name());
367 joint->set_group_type(EggGroup::GT_joint);
368
369 // The default_value originally passed to the CharacterJoint is what is used
370 // for skinning. However, the _default_value can be changed after joint
371 // construction (such as via a <DefaultPose>), so we can't just pull the
372 // current _default_value.
373 //
374 // We have to instead work back from the _initial_net_transform_inverse,
375 // which is computed at construction time from the original default_value:
376 //
377 // _net_transform = default_value * parent_joint->_net_transform;
378 // _initial_net_transform_inverse = invert(_net_transform);
379 //
380 // So we should be able to reconstruct the original default_value like so:
381 //
382 // default_value = invert(_initial_net_transform_inverse)
383 // * parent_joint->_initial_net_transform_inverse;
384 //
385 LMatrix4d net_transform = invert(LCAST(double, character_joint->_initial_net_transform_inverse));
386 if (parent_joint != nullptr) {
387 if (parent_joint->_initial_net_transform_inverse != character_joint->_initial_net_transform_inverse) {
388 LMatrix4d parent_inverse = LCAST(double, parent_joint->_initial_net_transform_inverse);
389 joint->add_matrix4(net_transform * parent_inverse);
390 }
391 } else if (!net_transform.is_identity()) {
392 joint->add_matrix4(net_transform);
393 }
394
395 // The joint's _default_value, if different, goes into a <DefaultPose>.
396 LMatrix4d default_pose = LCAST(double, character_joint->_default_value);
397 if (default_pose != joint->get_transform3d()) {
398 EggTransform transform;
399 transform.add_matrix4(LCAST(double, default_pose));
400 joint->set_default_pose(transform);
401 }
402
403 joint_group = joint;
404 egg_parent->add_child(joint_group);
405 if (joint_map != nullptr) {
406 CharacterJointMap::iterator mi = joint_map->find(character_joint);
407 if (mi != joint_map->end()) {
408 pvector<pair<EggVertex*,PN_stdfloat> > &joint_vertices = (*mi).second;
409 pvector<pair<EggVertex*,PN_stdfloat> >::const_iterator vi;
410 for (vi = joint_vertices.begin(); vi != joint_vertices.end(); ++vi) {
411 joint->set_vertex_membership((*vi).first, (*vi).second);
412 }
413 }
414 }
415 }
416
417 for (int i = 0; i < num_children ; i++) {
418 PartGroup *partGroup= bundleNode->get_child(i);
419 convert_character_bundle(partGroup, joint_group, joint_map, character_joint);
420 }
421
422}
423
424/**
425 * Converts the indicated Character to the corresponding Egg constructs.
426 */
427void EggSaver::
428convert_character_node(Character *node, const WorkingNodePath &node_path,
429 EggGroupNode *egg_parent, bool has_decal) {
430
431 // A sequence node gets converted to an ordinary EggGroup, we only apply the
432 // appropriate switch attributes to turn it into a sequence.
433 // We have to use DT_structured since it is the only mode that preserves the
434 // node hierarchy, including LODNodes and CollisionNodes that may be under
435 // this Character node.
436 EggGroup *egg_group = new EggGroup(node->get_name());
437 egg_group->set_dart_type(EggGroup::DT_structured);
438 egg_parent->add_child(egg_group);
439 apply_node_properties(egg_group, node);
440
441 CharacterJointMap joint_map;
442 recurse_nodes(node_path, egg_group, has_decal, &joint_map);
443
444 // turn it into a switch.. egg_group->set_switch_flag(true);
445
446 int num_bundles = node->get_num_bundles();
447 for (int i = 0; i < num_bundles; ++i) {
448 PartBundle *bundle = node->get_bundle(i);
449 convert_character_bundle(bundle, egg_group, &joint_map);
450 }
451}
452
453
454/**
455 * Converts the indicated CollisionNode to the corresponding Egg constructs.
456 */
457void EggSaver::
458convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path,
459 EggGroupNode *egg_parent, bool has_decal,
460 CharacterJointMap *joint_map) {
461 // A sequence node gets converted to an ordinary EggGroup, we only apply the
462 // appropriate switch attributes to turn it into a sequence
463 EggGroup *egg_group = new EggGroup(node->get_name());
464 egg_parent->add_child(egg_group);
465 apply_node_properties(egg_group, node, false);
466
467 // Set the collision masks, if present.
468 CollideMask from_mask = node->get_from_collide_mask();
469 CollideMask into_mask = node->get_into_collide_mask();
470 if (from_mask != CollisionNode::get_default_collide_mask() ||
472 if (from_mask == into_mask) {
473 egg_group->set_collide_mask(into_mask);
474 } else {
475 egg_group->set_from_collide_mask(from_mask);
476 egg_group->set_into_collide_mask(into_mask);
477 }
478 }
479
480 // turn it into a collision node
481 egg_group->set_collide_flags(EggGroup::CF_descend);
482
483 NodePath np = node_path.get_node_path();
484 CPT(TransformState) net_transform = np.get_net_transform();
485 LMatrix4 net_mat = net_transform->get_mat();
486 LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv());
487 net_mat = net_mat * inv;
488
489 int num_solids = node->get_num_solids();
490
491 if (num_solids > 0) {
492 // create vertex pool for collisions
493 EggVertexPool *cvpool = new EggVertexPool("vpool-collision");
494 egg_group->add_child(cvpool);
495
496 // traverse solids
497 for (int i = 0; i < num_solids; i++) {
498 CPT(CollisionSolid) child = node->get_solid(i);
499 int flags = EggGroup::CF_descend;
500
501 if (!child->is_tangible()) {
502 flags |= EggGroup::CF_intangible;
503 }
504
505 if (child->has_effective_normal() &&
506 child->get_effective_normal() == LVector3::up()) {
507 flags |= EggGroup::CF_level;
508 }
509
510 if (child->is_of_type(CollisionPolygon::get_class_type())) {
511 egg_group->set_cs_type(EggGroup::CST_polyset);
512 egg_group->set_collide_flags(flags);
513
514 EggPolygon *egg_poly = new EggPolygon;
515 egg_group->add_child(egg_poly);
516
517 CPT(CollisionPolygon) poly = DCAST(CollisionPolygon, child);
518 int num_points = poly->get_num_points();
519 for (int j = 0; j < num_points; j++) {
520 EggVertex egg_vert;
521 egg_vert.set_pos(LCAST(double, poly->get_point(j) * net_mat));
522 egg_vert.set_normal(LCAST(double, poly->get_normal() * net_mat));
523
524 EggVertex *new_egg_vert = cvpool->create_unique_vertex(egg_vert);
525 egg_poly->add_vertex(new_egg_vert);
526 }
527
528 } else if (child->is_of_type(CollisionSphere::get_class_type())) {
529 CPT(CollisionSphere) sphere = DCAST(CollisionSphere, child);
530 LPoint3 center = sphere->get_center();
531 PN_stdfloat radius = sphere->get_radius();
532
533 EggGroup *egg_sphere;
534 if (num_solids == 1) {
535 egg_sphere = egg_group;
536 } else {
537 egg_sphere = new EggGroup;
538 egg_group->add_child(egg_sphere);
539 }
540
541 if (child->is_of_type(CollisionInvSphere::get_class_type())) {
542 egg_sphere->set_cs_type(EggGroup::CST_inv_sphere);
543 } else {
544 egg_sphere->set_cs_type(EggGroup::CST_sphere);
545 }
546 egg_sphere->set_collide_flags(flags);
547
548 EggVertex ev1, ev2, ev3, ev4;
549 ev1.set_pos(LCAST(double, (center + LVector3(radius, 0, 0)) * net_mat));
550 ev2.set_pos(LCAST(double, (center + LVector3(0, radius, 0)) * net_mat));
551 ev3.set_pos(LCAST(double, (center + LVector3(-radius, 0, 0)) * net_mat));
552 ev4.set_pos(LCAST(double, (center + LVector3(0, -radius, 0)) * net_mat));
553
554 EggPolygon *egg_poly = new EggPolygon;
555 egg_sphere->add_child(egg_poly);
556
557 egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
558 egg_poly->add_vertex(cvpool->create_unique_vertex(ev2));
559 egg_poly->add_vertex(cvpool->create_unique_vertex(ev3));
560 egg_poly->add_vertex(cvpool->create_unique_vertex(ev4));
561
562 } else if (child->is_of_type(CollisionPlane::get_class_type())) {
563 LPlane plane = DCAST(CollisionPlane, child)->get_plane();
564 LPoint3 origin = plane.get_point();
565 LVector3 normal = plane.get_normal();
566
567 // Get an arbitrary vector on the plane by taking the cross product
568 // with any vector, as long as it is different.
569 LVector3 vec1;
570 if (std::fabs(normal[2]) > std::fabs(normal[1])) {
571 vec1 = normal.cross(LVector3(0, 1, 0));
572 } else {
573 vec1 = normal.cross(LVector3(0, 0, 1));
574 }
575
576 // Find a second vector perpendicular to the two.
577 LVector3 vec2 = normal.cross(vec1);
578
579 EggGroup *egg_plane;
580 if (num_solids == 1) {
581 egg_plane = egg_group;
582 } else {
583 egg_plane = new EggGroup;
584 egg_group->add_child(egg_plane);
585 }
586 egg_plane->set_cs_type(EggGroup::CST_plane);
587 egg_plane->set_collide_flags(flags);
588
589 EggVertex ev0, ev1, ev2;
590 ev0.set_pos(LCAST(double, origin * net_mat));
591 ev1.set_pos(LCAST(double, (origin + vec1) * net_mat));
592 ev2.set_pos(LCAST(double, (origin + vec2) * net_mat));
593
594 EggPolygon *egg_poly = new EggPolygon;
595 egg_plane->add_child(egg_poly);
596
597 egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
598 egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
599 egg_poly->add_vertex(cvpool->create_unique_vertex(ev2));
600
601 } else if (child->is_of_type(CollisionBox::get_class_type())) {
602 CPT(CollisionBox) box = DCAST(CollisionBox, child);
603 LPoint3 min_point = box->get_min();
604 LPoint3 max_point = box->get_max();
605
606 EggGroup *egg_box;
607 if (num_solids == 1) {
608 egg_box = egg_group;
609 } else {
610 egg_box = new EggGroup;
611 egg_group->add_child(egg_box);
612 }
613 egg_box->set_cs_type(EggGroup::CST_box);
614 egg_box->set_collide_flags(flags);
615
616 // Just add the min and max points.
617 EggVertex ev0, ev1;
618 ev0.set_pos(LCAST(double, min_point * net_mat));
619 ev1.set_pos(LCAST(double, max_point * net_mat));
620
621 EggLine *egg_poly = new EggLine;
622 egg_box->add_child(egg_poly);
623
624 egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
625 egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
626
627 } else if (child->is_of_type(CollisionCapsule::get_class_type())) {
628 CPT(CollisionCapsule) capsule = DCAST(CollisionCapsule, child);
629 LPoint3 point_a = capsule->get_point_a();
630 LPoint3 point_b = capsule->get_point_b();
631 LPoint3 centroid = (point_a + point_b) * 0.5f;
632
633 // Also get an arbitrary vector perpendicular to the capsule.
634 LVector3 axis = point_b - point_a;
635 LVector3 sideways;
636 if (std::fabs(axis[2]) > std::fabs(axis[1])) {
637 sideways = axis.cross(LVector3(0, 1, 0));
638 } else {
639 sideways = axis.cross(LVector3(0, 0, 1));
640 }
641 sideways.normalize();
642 sideways *= capsule->get_radius();
643 LVector3 extend = axis.normalized() * capsule->get_radius();
644
645 EggGroup *egg_capsule;
646 if (num_solids == 1) {
647 egg_capsule = egg_group;
648 } else {
649 egg_capsule = new EggGroup;
650 egg_group->add_child(egg_capsule);
651 }
652 egg_capsule->set_cs_type(EggGroup::CST_tube);
653 egg_capsule->set_collide_flags(flags);
654
655 // Add two points for the endcaps, and then two points around the
656 // centroid to indicate the radius.
657 EggVertex ev0, ev1, ev2, ev3;
658 ev0.set_pos(LCAST(double, (point_a - extend) * net_mat));
659 ev1.set_pos(LCAST(double, (centroid + sideways) * net_mat));
660 ev2.set_pos(LCAST(double, (point_b + extend) * net_mat));
661 ev3.set_pos(LCAST(double, (centroid - sideways) * net_mat));
662
663 EggPolygon *egg_poly = new EggPolygon;
664 egg_capsule->add_child(egg_poly);
665
666 egg_poly->add_vertex(cvpool->create_unique_vertex(ev0));
667 egg_poly->add_vertex(cvpool->create_unique_vertex(ev1));
668 egg_poly->add_vertex(cvpool->create_unique_vertex(ev2));
669 egg_poly->add_vertex(cvpool->create_unique_vertex(ev3));
670
671 } else {
672 nout << "Encountered unknown collision solid type " << child->get_type() << "\n";
673 }
674 }
675 }
676
677 // recurse over children - hm. do I need to do this?
678 recurse_nodes(node_path, egg_group, has_decal, joint_map);
679}
680
681/**
682 * Converts a GeomNode to the corresponding egg structures.
683 */
684void EggSaver::
685convert_geom_node(GeomNode *node, const WorkingNodePath &node_path,
686 EggGroupNode *egg_parent, bool has_decal, CharacterJointMap *joint_map) {
687 PT(EggGroup) egg_group = new EggGroup(node->get_name());
688 bool fancy_attributes = apply_node_properties(egg_group, node);
689
690 if (node->get_effects()->has_decal()) {
691 has_decal = true;
692 }
693
694 if (has_decal) {
695 egg_group->set_decal_flag(true);
696 }
697
698 if (fancy_attributes || has_decal || !node->get_name().empty()) {
699 // If we have any fancy attributes on the node, or if we're making decal
700 // geometry, we have to make a special node to hold the geometry (normally
701 // it would just appear within its parent).
702 egg_parent->add_child(egg_group.p());
703 egg_parent = egg_group;
704 }
705
706 NodePath np = node_path.get_node_path();
707 CPT(RenderState) net_state = np.get_net_state();
708 CPT(TransformState) net_transform = np.get_net_transform();
709 LMatrix4 net_mat = net_transform->get_mat();
710 LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv());
711 net_mat = net_mat * inv;
712
713 // Now get out all the various kinds of geometry.
714 int num_geoms = node->get_num_geoms();
715 for (int i = 0; i < num_geoms; ++i) {
716 CPT(RenderState) geom_state = node->get_geom_state(i);
717 CPT(RenderState) geom_net_state = net_state->compose(geom_state);
718
719 // If there is only one Geom, and the node has no state, apply the state
720 // attributes from the Geom to the group instead, so that we don't end up
721 // duplicating it for a lot of primitives.
722 if (num_geoms == 1 && node->get_num_children() == 0 && egg_parent == egg_group &&
723 !geom_state->is_empty() && node->get_state()->is_empty()) {
724 apply_state_properties(egg_group, geom_state);
725 geom_state = RenderState::make_empty();
726 }
727
728 const Geom *geom = node->get_geom(i);
729 int num_primitives = geom->get_num_primitives();
730 for (int j = 0; j < num_primitives; ++j) {
731 const GeomPrimitive *primitive = geom->get_primitive(j);
732 CPT(GeomPrimitive) simple = primitive->decompose();
733 CPT(GeomVertexData) vdata = geom->get_vertex_data();
734 // vdata = vdata->animate_vertices(true, Thread::get_current_thread());
735 convert_primitive(vdata, simple, geom_state, geom_net_state,
736 net_mat, egg_parent, joint_map);
737 }
738 }
739
740 recurse_nodes(node_path, egg_parent, has_decal, joint_map);
741}
742
743/**
744 *
745 */
746void EggSaver::
747convert_primitive(const GeomVertexData *vertex_data,
748 const GeomPrimitive *primitive,
749 const RenderState *geom_state, const RenderState *net_state,
750 const LMatrix4 &net_mat, EggGroupNode *egg_parent,
751 CharacterJointMap *joint_map) {
752 GeomVertexReader reader(vertex_data);
753 const GeomVertexFormat *format = vertex_data->get_format();
754
755 // Make a zygote that will be duplicated for each primitive.
756 PT(EggPrimitive) egg_prim;
757 if (primitive->is_of_type(GeomTriangles::get_class_type())) {
758 egg_prim = new EggPolygon();
759 } else if (primitive->is_of_type(GeomPatches::get_class_type())) {
760 egg_prim = new EggPatch();
761 } else if (primitive->is_of_type(GeomPoints::get_class_type())) {
762 egg_prim = new EggPoint();
763 } else if (primitive->is_of_type(GeomLines::get_class_type())) {
764 egg_prim = new EggLine();
765 } else {
766 // Huh, an unknown geometry type.
767 return;
768 }
769
770 // Apply render attributes.
771 apply_state_properties(egg_prim, geom_state);
772
773 // Check for a color scale.
774 LVecBase4 color_scale(1.0f, 1.0f, 1.0f, 1.0f);
775 const ColorScaleAttrib *csa;
776 if (net_state->get_attrib(csa)) {
777 color_scale = csa->get_scale();
778 }
779
780 // Check for a color override.
781 bool has_color_override = false;
782 bool has_color_off = false;
783 LColor color_override;
784 const ColorAttrib *ca;
785 if (net_state->get_attrib(ca)) {
786 if (ca->get_color_type() == ColorAttrib::T_flat) {
787 has_color_override = true;
788 color_override = ca->get_color();
789 color_override.set(color_override[0] * color_scale[0],
790 color_override[1] * color_scale[1],
791 color_override[2] * color_scale[2],
792 color_override[3] * color_scale[3]);
793
794 } else if (ca->get_color_type() == ColorAttrib::T_off) {
795 has_color_off = true;
796 }
797 }
798
799 // Check for a material.
800 EggMaterial *egg_mat = nullptr;
801 const MaterialAttrib *ma;
802 if (net_state->get_attrib(ma)) {
803 egg_mat = get_egg_material(ma->get_material());
804 if (egg_mat != nullptr) {
805 egg_prim->set_material(egg_mat);
806 }
807 }
808
809 // Check for a texture.
810 const TextureAttrib *ta;
811 if (net_state->get_attrib(ta)) {
812 for (size_t i = 0; i < (size_t)ta->get_num_on_stages(); ++i) {
813 TextureStage *tex_stage = ta->get_on_stage(i);
814
815 EggTexture *egg_tex = get_egg_texture(ta->get_on_texture(tex_stage));
816 if (egg_tex != nullptr) {
817 switch (tex_stage->get_mode()) {
818 case TextureStage::M_modulate:
819 if (has_color_off == true && i == 0) {
820 egg_tex->set_env_type(EggTexture::ET_replace);
821 } else {
822 egg_tex->set_env_type(EggTexture::ET_modulate);
823 }
824 break;
825 case TextureStage::M_decal:
826 egg_tex->set_env_type(EggTexture::ET_decal);
827 break;
828 case TextureStage::M_blend:
829 egg_tex->set_env_type(EggTexture::ET_blend);
830 break;
831 case TextureStage::M_replace:
832 egg_tex->set_env_type(EggTexture::ET_replace);
833 break;
834 case TextureStage::M_add:
835 egg_tex->set_env_type(EggTexture::ET_add);
836 break;
837 case TextureStage::M_blend_color_scale:
838 egg_tex->set_env_type(EggTexture::ET_blend_color_scale);
839 break;
840 case TextureStage::M_modulate_glow:
841 egg_tex->set_env_type(EggTexture::ET_modulate_glow);
842 break;
843 case TextureStage::M_modulate_gloss:
844 egg_tex->set_env_type(EggTexture::ET_modulate_gloss);
845 break;
846 case TextureStage::M_normal:
847 egg_tex->set_env_type(EggTexture::ET_normal);
848 break;
849 case TextureStage::M_normal_height:
850 egg_tex->set_env_type(EggTexture::ET_normal_height);
851 break;
852 case TextureStage::M_glow:
853 egg_tex->set_env_type(EggTexture::ET_glow);
854 break;
855 case TextureStage::M_gloss:
856 egg_tex->set_env_type(EggTexture::ET_gloss);
857 break;
858 case TextureStage::M_height:
859 egg_tex->set_env_type(EggTexture::ET_height);
860 break;
861 case TextureStage::M_selector:
862 egg_tex->set_env_type(EggTexture::ET_selector);
863 break;
864 case TextureStage::M_normal_gloss:
865 egg_tex->set_env_type(EggTexture::ET_normal_gloss);
866 break;
867 case TextureStage::M_emission:
868 egg_tex->set_env_type(EggTexture::ET_emission);
869 break;
870 default:
871 break;
872 }
873
874 const InternalName *name = tex_stage->get_texcoord_name();
875 if (name != nullptr && name != InternalName::get_texcoord()) {
876 egg_tex->set_uv_name(name->get_basename());
877 }
878
879 egg_prim->add_texture(egg_tex);
880 }
881 }
882 }
883
884 // Check the backface flag.
885 const CullFaceAttrib *cfa;
886 if (net_state->get_attrib(cfa)) {
887 if (cfa->get_effective_mode() == CullFaceAttrib::M_cull_none) {
888 egg_prim->set_bface_flag(true);
889 }
890 }
891
892 // Check for line thickness and such.
893 const RenderModeAttrib *rma;
894 if (net_state->get_attrib(rma)) {
895 if (egg_prim->is_of_type(EggPoint::get_class_type())) {
896 EggPoint *egg_point = (EggPoint *)egg_prim.p();
897 egg_point->set_thick(rma->get_thickness());
898 egg_point->set_perspective(rma->get_perspective());
899
900 } else if (egg_prim->is_of_type(EggLine::get_class_type())) {
901 EggLine *egg_line = (EggLine *)egg_prim.p();
902 egg_line->set_thick(rma->get_thickness());
903 }
904 }
905
906 CPT(TransformBlendTable) transformBlendTable = vertex_data->get_transform_blend_table();
907
908 int num_primitives = primitive->get_num_primitives();
909 int num_vertices = primitive->get_num_vertices_per_primitive();
910
911 for (int i = 0; i < num_primitives; ++i) {
912 PT(EggPrimitive) egg_child = egg_prim->make_copy();
913 egg_parent->add_child(egg_child);
914
915 for (int j = 0; j < num_vertices; j++) {
916 EggVertex egg_vert;
917
918 // Get per-vertex properties.
919 reader.set_row(primitive->get_vertex(i * num_vertices + j));
920
921 reader.set_column(InternalName::get_vertex());
922 LVertex vertex = reader.get_data3();
923 egg_vert.set_pos(LCAST(double, vertex * net_mat));
924
925 if (vertex_data->has_column(InternalName::get_normal())) {
926 reader.set_column(InternalName::get_normal());
927 LNormal normal = reader.get_data3();
928 egg_vert.set_normal(LCAST(double, normal * net_mat));
929 }
930 if (has_color_override) {
931 egg_vert.set_color(color_override);
932
933 } else if (!has_color_off) {
934 LColor color(1.0f, 1.0f, 1.0f, 1.0f);
935 if (vertex_data->has_column(InternalName::get_color())) {
936 reader.set_column(InternalName::get_color());
937 color = reader.get_data4();
938 }
939 egg_vert.set_color(LColor(color[0] * color_scale[0],
940 color[1] * color_scale[1],
941 color[2] * color_scale[2],
942 color[3] * color_scale[3]));
943 }
944
945 for (size_t ti = 0; ti < format->get_num_texcoords(); ++ti) {
946 const InternalName *texcoord_name = format->get_texcoord(ti);
947 reader.set_column(texcoord_name);
948 LTexCoord uv = reader.get_data2();
949 if (texcoord_name == InternalName::get_texcoord()) {
950 egg_vert.set_uv(LCAST(double, uv));
951 } else {
952 egg_vert.set_uv(texcoord_name->get_basename(), LCAST(double, uv));
953 }
954 }
955
956 EggVertex *new_egg_vert = _vpool->create_unique_vertex(egg_vert);
957
958 if (vertex_data->has_column(InternalName::get_transform_blend()) &&
959 joint_map != nullptr && transformBlendTable != nullptr) {
960 reader.set_column(InternalName::get_transform_blend());
961 int idx = reader.get_data1i();
962 const TransformBlend &blend = transformBlendTable->get_blend(idx);
963 int num_weights = blend.get_num_transforms();
964 for (int k = 0; k < num_weights; ++k) {
965 PN_stdfloat weight = blend.get_weight(k);
966 if (weight!=0) {
967 const VertexTransform *vertex_transform = blend.get_transform(k);
968 if (vertex_transform->is_of_type(JointVertexTransform::get_class_type())) {
969 const JointVertexTransform *joint_vertex_transform = DCAST(const JointVertexTransform, vertex_transform);
970
971 CharacterJointMap::iterator mi = joint_map->find(joint_vertex_transform->get_joint());
972 if (mi == joint_map->end()) {
973 mi = joint_map->insert(CharacterJointMap::value_type(joint_vertex_transform->get_joint(), pvector<pair<EggVertex*,PN_stdfloat> >())).first;
974 }
975 pvector<pair<EggVertex*,PN_stdfloat> > &joint_vertices = (*mi).second;
976 joint_vertices.push_back(pair<EggVertex*,PN_stdfloat>(new_egg_vert, weight));
977 }
978 }
979 }
980 }
981
982 egg_child->add_vertex(new_egg_vert);
983 }
984 }
985}
986
987/**
988 * Converts all the children of the indicated node.
989 */
990void EggSaver::
991recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent,
992 bool has_decal, CharacterJointMap *joint_map) {
993 PandaNode *node = node_path.node();
994 int num_children = node->get_num_children();
995
996 for (int i = 0; i < num_children; i++) {
997 PandaNode *child = node->get_child(i);
998 convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal, joint_map);
999 }
1000}
1001
1002/**
1003 * Applies any special properties that might be stored on the node, like
1004 * billboarding. Returns true if any were applied, false otherwise.
1005 */
1006bool EggSaver::
1007apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage) {
1008 bool any_applied = false;
1009
1010 if (node->is_overall_hidden() && allow_backstage) {
1011 // This node is hidden. We'll go ahead and convert it, but we'll put in
1012 // the "backstage" flag to mean it's not real geometry. unless the caller
1013 // wants to keep it (by setting allow_backstage to false)
1014 egg_group->add_object_type("backstage");
1015 }
1016
1017 if (node->has_tags()) {
1018 if (apply_tags(egg_group, node)) {
1019 any_applied = true;
1020 }
1021 }
1022
1023 if (node->is_of_type(ModelNode::get_class_type())) {
1024 ModelNode *model_node = DCAST(ModelNode, node);
1025 switch (model_node->get_preserve_transform()) {
1026 case ModelNode::PT_none:
1027 egg_group->set_model_flag(true);
1028 break;
1029
1030 case ModelNode::PT_drop_node:
1031 break;
1032
1033 case ModelNode::PT_net:
1034 egg_group->set_dcs_type(EggGroup::DC_net);
1035 break;
1036
1037 case ModelNode::PT_local:
1038 egg_group->set_dcs_type(EggGroup::DC_local);
1039 break;
1040
1041 case ModelNode::PT_no_touch:
1042 egg_group->set_dcs_type(EggGroup::DC_no_touch);
1043 break;
1044 }
1045 }
1046
1047 if (node->is_of_type(UvScrollNode::get_class_type())) {
1048 const UvScrollNode *scroll_node = (const UvScrollNode *)node;
1049 egg_group->set_scroll_u(scroll_node->get_u_speed());
1050 egg_group->set_scroll_v(scroll_node->get_v_speed());
1051 egg_group->set_scroll_w(scroll_node->get_w_speed());
1052 egg_group->set_scroll_r(scroll_node->get_r_speed());
1053 }
1054
1055 const RenderEffects *effects = node->get_effects();
1056 const RenderEffect *effect = effects->get_effect(BillboardEffect::get_class_type());
1057 if (effect != nullptr) {
1058 const BillboardEffect *bbe = DCAST(BillboardEffect, effect);
1059 if (bbe->get_axial_rotate()) {
1060 egg_group->set_billboard_type(EggGroup::BT_axis);
1061 any_applied = true;
1062
1063 } else if (bbe->get_eye_relative()) {
1064 egg_group->set_billboard_type(EggGroup::BT_point_camera_relative);
1065 any_applied = true;
1066
1067 } else {
1068 egg_group->set_billboard_type(EggGroup::BT_point_world_relative);
1069 any_applied = true;
1070 }
1071 }
1072
1073 const TransformState *transform = node->get_transform();
1074 if (!transform->is_identity()) {
1075 if (transform->has_components()) {
1076 // If the transform can be represented componentwise, we prefer storing
1077 // it that way in the egg file.
1078 const LVecBase3 &scale = transform->get_scale();
1079 const LQuaternion &quat = transform->get_quat();
1080 const LVecBase3 &pos = transform->get_pos();
1081 if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) {
1082 egg_group->add_scale3d(LCAST(double, scale));
1083 }
1084 if (!quat.is_identity()) {
1085 egg_group->add_rotate3d(LCAST(double, quat));
1086 }
1087 if (!pos.almost_equal(LVecBase3::zero())) {
1088 egg_group->add_translate3d(LCAST(double, pos));
1089 }
1090
1091 } else if (transform->has_mat()) {
1092 // Otherwise, we store the raw matrix.
1093 const LMatrix4 &mat = transform->get_mat();
1094 egg_group->set_transform3d(LCAST(double, mat));
1095 }
1096 any_applied = true;
1097 }
1098
1099 const RenderState *state = node->get_state();
1100 if (apply_state_properties(egg_group, state)) {
1101 return true;
1102 }
1103
1104 return any_applied;
1105}
1106
1107/**
1108 * Applies any special render state settings on the primitive or group.
1109 * Returns true if any were applied, false otherwise.
1110 */
1111bool EggSaver::
1112apply_state_properties(EggRenderMode *egg_render_mode, const RenderState *state) {
1113 if (state->is_empty()) {
1114 return false;
1115 }
1116
1117 bool any_applied = false;
1118
1119 // Check the transparency mode.
1120 const TransparencyAttrib *tra;
1121 if (state->get_attrib(tra)) {
1122 EggRenderMode::AlphaMode tex_trans = EggRenderMode::AM_unspecified;
1123 switch (tra->get_mode()) {
1124 case TransparencyAttrib::M_none:
1125 tex_trans = EggRenderMode::AM_off;
1126 break;
1127 case TransparencyAttrib::M_alpha:
1128 tex_trans = EggRenderMode::AM_blend;
1129 break;
1130 case TransparencyAttrib::M_premultiplied_alpha:
1131 tex_trans = EggRenderMode::AM_premultiplied;
1132 break;
1133 case TransparencyAttrib::M_multisample:
1134 tex_trans = EggRenderMode::AM_ms;
1135 break;
1136 case TransparencyAttrib::M_multisample_mask:
1137 tex_trans = EggRenderMode::AM_ms_mask;
1138 break;
1139 case TransparencyAttrib::M_binary:
1140 tex_trans = EggRenderMode::AM_binary;
1141 break;
1142 case TransparencyAttrib::M_dual:
1143 tex_trans = EggRenderMode::AM_dual;
1144 break;
1145 default: // intentional fall-through
1146 break;
1147 }
1148 egg_render_mode->set_alpha_mode(tex_trans);
1149 }
1150
1151 const DepthWriteAttrib *dwa;
1152 if (state->get_attrib(dwa)) {
1153 if (dwa->get_mode() != DepthWriteAttrib::M_off) {
1154 egg_render_mode->set_depth_write_mode(EggRenderMode::DWM_on);
1155
1156 } else if (egg_render_mode->get_alpha_mode() == EggRenderMode::AM_blend) {
1157 // AM_blend_no_occlude is like AM_blend but also implies DWM_off.
1158 egg_render_mode->set_alpha_mode(EggRenderMode::AM_blend_no_occlude);
1159
1160 } else {
1161 egg_render_mode->set_depth_write_mode(EggRenderMode::DWM_off);
1162 }
1163 any_applied = true;
1164 }
1165
1166 const DepthTestAttrib *dta;
1167 if (state->get_attrib(dta)) {
1168 RenderAttrib::PandaCompareFunc mode = dta->get_mode();
1169 if (mode == DepthTestAttrib::M_none || mode == DepthTestAttrib::M_always) {
1170 egg_render_mode->set_depth_test_mode(EggRenderMode::DTM_off);
1171 } else {
1172 egg_render_mode->set_depth_test_mode(EggRenderMode::DTM_on);
1173 }
1174 any_applied = true;
1175 }
1176
1177 const DepthOffsetAttrib *doa;
1178 if (state->get_attrib(doa)) {
1179 egg_render_mode->set_depth_offset(doa->get_offset());
1180 any_applied = true;
1181 }
1182
1183 const CullBinAttrib *cba;
1184 if (state->get_attrib(cba)) {
1185 egg_render_mode->set_bin(cba->get_bin_name());
1186 egg_render_mode->set_draw_order(cba->get_draw_order());
1187 any_applied = true;
1188 }
1189
1190 return any_applied;
1191}
1192
1193/**
1194 * Applies string tags to the egg file. Returns true if any were applied,
1195 * false otherwise.
1196 */
1197bool EggSaver::
1198apply_tags(EggGroup *egg_group, PandaNode *node) {
1199 bool any_applied = false;
1200
1201 vector_string keys;
1202 node->get_tag_keys(keys);
1203
1204 for (size_t i = 0; i < keys.size(); ++i) {
1205 if (apply_tag(egg_group, node, keys[i])) {
1206 any_applied = true;
1207 }
1208 }
1209 return any_applied;
1210}
1211
1212/**
1213 * Applies the named string tags to the egg file.
1214 */
1215bool EggSaver::
1216apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag) {
1217 if (!node->has_tag(tag)) {
1218 return false;
1219 }
1220
1221 string value = node->get_tag(tag);
1222 egg_group->set_tag(tag, value);
1223 return true;
1224}
1225
1226/**
1227 * Returns an EggMaterial pointer that corresponds to the indicated Material.
1228 */
1229EggMaterial *EggSaver::
1230get_egg_material(Material *mat) {
1231 if (mat != nullptr) {
1232 EggMaterial temp(mat->get_name());
1233 if (mat->has_base_color()) {
1234 temp.set_base(mat->get_base_color());
1235 }
1236
1237 if (mat->has_ambient()) {
1238 temp.set_amb(mat->get_ambient());
1239 }
1240
1241 if (mat->has_diffuse()) {
1242 temp.set_diff(mat->get_diffuse());
1243 }
1244
1245 if (mat->has_specular()) {
1246 temp.set_spec(mat->get_specular());
1247 }
1248
1249 if (mat->has_emission()) {
1250 temp.set_emit(mat->get_emission());
1251 }
1252
1253 if (mat->has_roughness()) {
1254 temp.set_roughness(mat->get_roughness());
1255 } else {
1256 temp.set_shininess(mat->get_shininess());
1257 }
1258
1259 if (mat->has_metallic()) {
1260 temp.set_metallic(mat->get_metallic());
1261 }
1262
1263 if (mat->has_refractive_index()) {
1264 temp.set_ior(mat->get_refractive_index());
1265 }
1266
1267 temp.set_local(mat->get_local());
1268
1269 return _materials.create_unique_material(temp, ~EggMaterial::E_mref_name);
1270 }
1271
1272 return nullptr;
1273}
1274
1275/**
1276 * Returns an EggTexture pointer that corresponds to the indicated Texture.
1277 */
1278EggTexture *EggSaver::
1279get_egg_texture(Texture *tex) {
1280 if (tex != nullptr) {
1281 if (tex->has_filename()) {
1282 Filename filename = tex->get_filename();
1283 EggTexture temp(filename.get_basename_wo_extension(), filename);
1284 if (tex->has_alpha_filename()) {
1285 Filename alpha = tex->get_alpha_filename();
1286 temp.set_alpha_filename(alpha);
1287 }
1288
1289 switch (tex->get_minfilter()) {
1290 case SamplerState::FT_nearest:
1291 temp.set_minfilter(EggTexture::FT_nearest);
1292 break;
1293 case SamplerState::FT_linear:
1294 temp.set_minfilter(EggTexture::FT_linear);
1295 break;
1296 case SamplerState::FT_nearest_mipmap_nearest:
1297 temp.set_minfilter(EggTexture::FT_nearest_mipmap_nearest);
1298 break;
1299 case SamplerState::FT_linear_mipmap_nearest:
1300 temp.set_minfilter(EggTexture::FT_linear_mipmap_nearest);
1301 break;
1302 case SamplerState::FT_nearest_mipmap_linear:
1303 temp.set_minfilter(EggTexture::FT_nearest_mipmap_linear);
1304 break;
1305 case SamplerState::FT_linear_mipmap_linear:
1306 temp.set_minfilter(EggTexture::FT_linear_mipmap_linear);
1307 break;
1308
1309 default:
1310 break;
1311 }
1312
1313 switch (tex->get_magfilter()) {
1314 case SamplerState::FT_nearest:
1315 temp.set_magfilter(EggTexture::FT_nearest);
1316 break;
1317 case SamplerState::FT_linear:
1318 temp.set_magfilter(EggTexture::FT_linear);
1319 break;
1320
1321 default:
1322 break;
1323 }
1324
1325 switch (tex->get_wrap_u()) {
1326 case SamplerState::WM_clamp:
1327 temp.set_wrap_u(EggTexture::WM_clamp);
1328 break;
1329 case SamplerState::WM_repeat:
1330 temp.set_wrap_u(EggTexture::WM_repeat);
1331 break;
1332
1333 default:
1334 // There are some new wrap options on Texture that aren't yet
1335 // supported in egg.
1336 break;
1337 }
1338
1339 switch (tex->get_wrap_v()) {
1340 case SamplerState::WM_clamp:
1341 temp.set_wrap_v(EggTexture::WM_clamp);
1342 break;
1343 case SamplerState::WM_repeat:
1344 temp.set_wrap_v(EggTexture::WM_repeat);
1345 break;
1346
1347 default:
1348 // There are some new wrap options on Texture that aren't yet
1349 // supported in egg.
1350 break;
1351 }
1352
1353 switch (tex->get_format()) {
1354 case Texture::F_red:
1355 temp.set_format(EggTexture::F_red);
1356 break;
1357 case Texture::F_green:
1358 temp.set_format(EggTexture::F_green);
1359 break;
1360 case Texture::F_blue:
1361 temp.set_format(EggTexture::F_blue);
1362 break;
1363 case Texture::F_alpha:
1364 temp.set_format(EggTexture::F_alpha);
1365 break;
1366 case Texture::F_rgb:
1367 temp.set_format(EggTexture::F_rgb);
1368 break;
1369 case Texture::F_rgb5:
1370 temp.set_format(EggTexture::F_rgb5);
1371 break;
1372 case Texture::F_rgb8:
1373 temp.set_format(EggTexture::F_rgb8);
1374 break;
1375 case Texture::F_rgb12:
1376 temp.set_format(EggTexture::F_rgb12);
1377 break;
1378 case Texture::F_rgb332:
1379 temp.set_format(EggTexture::F_rgb332);
1380 break;
1381 case Texture::F_rgba:
1382 temp.set_format(EggTexture::F_rgba);
1383 break;
1384 case Texture::F_rgbm:
1385 temp.set_format(EggTexture::F_rgbm);
1386 break;
1387 case Texture::F_rgba4:
1388 temp.set_format(EggTexture::F_rgba4);
1389 break;
1390 case Texture::F_rgba5:
1391 temp.set_format(EggTexture::F_rgba5);
1392 break;
1393 case Texture::F_rgba8:
1394 temp.set_format(EggTexture::F_rgba8);
1395 break;
1396 case Texture::F_rgba12:
1397 temp.set_format(EggTexture::F_rgba12);
1398 break;
1399 case Texture::F_luminance:
1400 temp.set_format(EggTexture::F_luminance);
1401 break;
1402 case Texture::F_luminance_alpha:
1403 temp.set_format(EggTexture::F_luminance_alpha);
1404 break;
1405 case Texture::F_luminance_alphamask:
1406 temp.set_format(EggTexture::F_luminance_alphamask);
1407 break;
1408 case Texture::F_srgb:
1409 temp.set_format(EggTexture::F_srgb);
1410 break;
1411 case Texture::F_srgb_alpha:
1412 temp.set_format(EggTexture::F_srgb_alpha);
1413 break;
1414 default:
1415 break;
1416 }
1417
1418 return _textures.create_unique_texture(temp, ~EggTexture::E_tref_name);
1419 }
1420 }
1421
1422 return nullptr;
1423}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
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
has_table
Returns true if the indicated subtable has been assigned.
get_table
Returns a pointer to the indicated subtable's data, if it exists, or NULL if it does not.
This is the base class for AnimChannel and AnimBundle.
Definition animGroup.h:33
get_child
Returns the nth child of the group.
Definition animGroup.h:45
get_num_children
Returns the number of child nodes of the group.
Definition animGroup.h:45
bool get_eye_relative() const
Returns true if this billboard interprets the up vector relative to the camera, or false if it is rel...
bool get_axial_rotate() const
Returns true if this billboard rotates only around the axis of the up vector, or false if it rotates ...
This represents one joint of the character's animation, containing an animating transform matrix.
An animated character, with skeleton-morph animation and either soft- skinned or hard-skinned vertice...
Definition character.h:38
A node in the scene graph that can hold any number of CollisionSolids.
get_into_collide_mask
Returns the current "into" CollideMask.
get_from_collide_mask
Returns the current "from" CollideMask.
get_default_collide_mask
Returns the default into_collide_mask assigned to new CollisionNodes.
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
get_color_type
Returns the type of color specified by this ColorAttrib.
Definition colorAttrib.h:46
get_scale
Returns the scale to be applied to colors.
get_draw_order
Returns the draw order this attribute specifies.
get_bin_name
Returns the name of the bin this attribute specifies.
get_effective_mode
Returns the effective culling mode.
get_offset
Returns the depth offset represented by this attrib.
get_mode
Returns the depth write mode.
get_mode
Returns the depth write mode.
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition eggData.h:37
A base class for nodes in the hierarchy that are not leaf nodes.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition eggGroup.h:34
void set_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_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
set_default_pose
Replaces the initial pose transform.
Definition eggGroup.h:320
const LMatrix4d & get_vertex_frame_inv() const
Returns the inverse of the matrix returned by get_vertex_frame().
Definition eggNode.I:135
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
This class stores miscellaneous rendering properties that is associated with geometry,...
void set_draw_order(int order)
Sets the "draw-order" flag associated with this object.
void set_depth_write_mode(DepthWriteMode mode)
Specifies whether writes should be made to the depth buffer (assuming the rendering backend provides ...
void set_alpha_mode(AlphaMode mode)
Specifies precisely how the transparency for this geometry should be achieved, or if it should be use...
void set_depth_test_mode(DepthTestMode mode)
Specifies whether this geometry should be tested against the depth buffer when it is drawn (assuming ...
void set_bin(const std::string &bin)
Sets the "bin" string for this particular object.
AlphaMode get_alpha_mode() const
Returns the alpha mode that was set, or AM_unspecified if nothing was set.
void set_depth_offset(int bias)
Sets the "depth-offset" flag associated with this object.
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
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
Defines a texture map that may be applied to geometry.
Definition eggTexture.h:30
set_uv_name
Specifies the named set of texture coordinates that this texture will use when it is applied to geome...
Definition eggTexture.h:346
const LMatrix4d & get_transform3d() const
Returns the overall transform as a 4x4 matrix.
void add_scale3d(const LVecBase3d &scale)
Appends a possibly non-uniform scale to the current transform.
void set_transform3d(const LMatrix4d &mat)
Sets the overall transform as a 4x4 matrix.
void add_matrix4(const LMatrix4d &mat)
Appends an arbitrary 4x4 matrix to the current transform.
void add_translate3d(const LVector3d &translate)
Appends a 3-d translation operation to the current transform.
void add_rotate3d(double angle, const LVector3d &axis)
Appends a 3-d rotation about an arbitrary axis to the current transform.
A collection of vertices.
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 set_pos(double pos)
Sets the vertex position.
Definition eggVertex.I:42
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition eggVertex.I:193
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.
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition filename.I:386
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
get_num_geoms
Returns the number of geoms in the node.
Definition geomNode.h:71
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition geomNode.h:75
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
int get_num_primitives() const
Returns the number of individual primitives stored within this object.
get_vertex
Returns the ith vertex index in the table.
ConstPointerTo< GeomPrimitive > decompose() const
Decomposes a complex primitive type into a simpler primitive type, for instance triangle strips to tr...
get_num_vertices_per_primitive
If the primitive type is a simple type in which all primitives have the same number of vertices,...
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
bool has_column(const InternalName *name) const
Returns true if the data has the named column, false otherwise.
get_format
Returns a pointer to the GeomVertexFormat structure that defines this data.
get_texcoord
Returns the name of the nth texcoord column.
get_num_texcoords
Returns the number of columns within the format that represent texture coordinates.
get_basename
Return the name represented by just this particular InternalName object, ignoring its parents names.
const CharacterJoint * get_joint() const
Returns the joint for which this object returns the transform.
A Level-of-Detail node.
Definition lodNode.h:28
get_out
Returns the "out" distance of the indicated switch range.
Definition lodNode.h:61
get_in
Returns the "in" distance of the indicated switch range.
Definition lodNode.h:59
get_center
Returns the center of the LOD.
Definition lodNode.h:83
get_num_switches
Returns the number of switch ranges added to the LODNode.
Definition lodNode.h:59
get_material
If the MaterialAttrib is not an 'off' MaterialAttrib, returns the material that is associated.
Defines the way an object appears in the presence of lighting.
Definition material.h:43
PreserveTransform get_preserve_transform() const
Returns the current setting of the preserve_transform flag.
Definition modelNode.I:61
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
virtual bool is_geom_node() const
A simple downcast check.
void get_tag_keys(vector_string &keys) const
Fills the given vector up with the list of tags on this PandaNode.
is_overall_hidden
Returns true if the node has been hidden to all cameras by clearing its overall bit.
Definition pandaNode.h:248
get_child
Returns the nth child node of this node.
Definition pandaNode.h:124
get_num_children
Returns the number of child nodes this node has.
Definition pandaNode.h:124
This is the base class for PartRoot and MovingPart.
Definition partGroup.h:43
get_num_children
Returns the number of child nodes of the group.
Definition partGroup.h:72
get_child
Returns the nth child of the group.
Definition partGroup.h:72
const RenderEffect * get_effect(size_t n) const
Returns the nth effect in the state.
get_perspective
Returns the perspective flag.
get_thickness
Returns the line width or point thickness.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
bool is_empty() const
Returns true if the state is empty, false otherwise.
Definition renderState.I:27
A node that automatically cycles through rendering each one of its children according to its frame ra...
A node that renders only one of its children, according to the user's indication.
Definition switchNode.h:25
get_num_on_stages
Returns the number of stages that are turned on by the attribute.
get_on_texture
Returns the texture associated with the indicated stage, or NULL if no texture is associated.
get_on_stage
Returns the nth stage turned on by the attribute, sorted in render order.
get_mode
Return the mode of this stage.
get_texcoord_name
See set_texcoord_name.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition texture.h:371
get_minfilter
Returns the filter mode of the texture for minification.
Definition texture.h:392
has_filename
Returns true if the filename has been set and is available.
Definition texture.h:321
get_filename
Returns the filename that has been set.
Definition texture.h:321
get_alpha_filename
Returns the alpha_filename that has been set.
Definition texture.h:327
get_magfilter
Returns the filter mode of the texture for magnification.
Definition texture.h:398
has_alpha_filename
Returns true if the alpha_filename has been set and is available.
Definition texture.h:327
get_wrap_u
Returns the wrap mode of the texture in the U direction.
Definition texture.h:379
get_wrap_v
Returns the wrap mode of the texture in the V direction.
Definition texture.h:383
get_num_transforms
Returns the number of transforms stored in the blend object.
get_weight
Returns the weight associated with the indicated transform, or 0 if there is no entry for the transfo...
get_transform
Returns the nth transform stored in the blend object.
get_quat
Returns the rotation component of the transform as a quaternion.
bool has_mat() const
Returns true if the transform can be described as a matrix.
get_scale
Returns the scale component of the transform.
bool has_components() const
Returns true if the transform can be described by separate pos, hpr, and scale components.
get_mat
Returns the matrix that describes the transform.
bool is_identity() const
Returns true if the transform represents the identity matrix, false otherwise.
get_pos
Returns the pos component of the transform.
get_mode
Returns the transparency mode.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
This is a class designed to support low-overhead traversals of the complete scene graph,...
PandaNode * node() const
Returns the node traversed to so far.
get_node_path
Constructs and returns an actual NodePath that represents the same path we have just traversed.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
STTransform::operator CPT TransformState() const
This is used internally to convert an STTransform into a TransformState pointer.
Definition stTransform.I:98
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.