Panda3D
Loading...
Searching...
No Matches
character.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 character.cxx
10 * @author drose
11 * @date 2002-03-06
12 */
13
14#include "character.h"
15#include "characterJoint.h"
16#include "config_char.h"
17#include "nodePath.h"
18#include "geomNode.h"
19#include "datagram.h"
20#include "datagramIterator.h"
21#include "bamReader.h"
22#include "bamWriter.h"
23#include "pStatTimer.h"
24#include "animControl.h"
25#include "clockObject.h"
26#include "pStatTimer.h"
27#include "camera.h"
28#include "cullTraverser.h"
29#include "cullTraverserData.h"
30
31TypeHandle Character::_type_handle;
32
33PStatCollector Character::_animation_pcollector("*:Animation");
34
35/**
36 * Use make_copy() or copy_subgraph() to copy a Character.
37 */
38Character::
39Character(const Character &copy, bool copy_bundles) :
40 PartBundleNode(copy),
41 _lod_center(copy._lod_center),
42 _lod_far_distance(copy._lod_far_distance),
43 _lod_near_distance(copy._lod_near_distance),
44 _lod_delay_factor(copy._lod_delay_factor),
45 _do_lod_animation(copy._do_lod_animation),
46 _joints_pcollector(copy._joints_pcollector),
47 _skinning_pcollector(copy._skinning_pcollector)
48{
49 set_cull_callback();
50
51 if (copy_bundles) {
52 // Copy the bundle(s).
53 int num_bundles = copy.get_num_bundles();
54 for (int i = 0; i < num_bundles; ++i) {
55 PartBundle *orig_bundle = copy.get_bundle(i);
56 PT(PartBundle) new_bundle = DCAST(PartBundle, orig_bundle->copy_subgraph());
57 add_bundle(new_bundle);
58 }
59 } else {
60 // Share the bundles.
61 int num_bundles = copy.get_num_bundles();
62 for (int i = 0; i < num_bundles; ++i) {
63 PartBundle *orig_bundle = copy.get_bundle(i);
64 add_bundle(orig_bundle);
65 }
66 }
67 _last_auto_update = -1.0;
68 _view_frame = -1;
69 _view_distance2 = 0.0f;
70}
71
72/**
73 *
74 */
75Character::
76Character(const std::string &name) :
77 PartBundleNode(name, new CharacterJointBundle(name)),
78 _joints_pcollector(PStatCollector(_animation_pcollector, name), "Joints"),
79 _skinning_pcollector(PStatCollector(_animation_pcollector, name), "Vertices")
80{
81 set_cull_callback();
82 clear_lod_animation();
83 _last_auto_update = -1.0;
84 _view_frame = -1;
85 _view_distance2 = 0.0f;
86}
87
88/**
89 *
90 */
91Character::
92~Character() {
93 int num_bundles = get_num_bundles();
94 for (int i = 0; i < num_bundles; ++i) {
95 r_clear_joint_characters(get_bundle(i));
96 }
97}
98
99/**
100 * The Character make_copy() function will make a new copy of the Character,
101 * with all of its joints copied, and with a new set of dynamic vertex arrays
102 * all ready to go, but it will not copy any of the original Character's
103 * geometry, so the new Character won't look like much. Use copy_subgraph()
104 * to make a full copy of the Character.
105 */
107make_copy() const {
108 return new Character(*this, true);
109}
110
111/**
112 * This is similar to make_copy(), but it makes a copy for the specific
113 * purpose of flatten. Typically, this will be a new PandaNode with a new
114 * pointer, but all of the internal data will always be shared with the
115 * original; whereas the new node returned by make_copy() might not share the
116 * internal data.
117 */
119dupe_for_flatten() const {
120 return new Character(*this, false);
121}
122
123/**
124 * Collapses this node with the other node, if possible, and returns a pointer
125 * to the combined node, or NULL if the two nodes cannot safely be combined.
126 *
127 * The return value may be this, other, or a new node altogether.
128 *
129 * This function is called from GraphReducer::flatten(), and need not deal
130 * with children; its job is just to decide whether to collapse the two nodes
131 * and what the collapsed node should look like.
132 */
134combine_with(PandaNode *other) {
135 if (is_exact_type(get_class_type()) &&
136 other->is_exact_type(get_class_type())) {
137 // Two Characters can combine by moving PartBundles from one to the other.
138 Character *c_other = DCAST(Character, other);
139 steal_bundles(c_other);
140 return this;
141 }
142
143 return PandaNode::combine_with(other);
144}
145
146/**
147 * This function will be called during the cull traversal to perform any
148 * additional operations that should be performed at cull time. This may
149 * include additional manipulation of render state or additional
150 * visible/invisible decisions, or any other arbitrary operation.
151 *
152 * Note that this function will *not* be called unless set_cull_callback() is
153 * called in the constructor of the derived class. It is necessary to call
154 * set_cull_callback() to indicated that we require cull_callback() to be
155 * called.
156 *
157 * By the time this function is called, the node has already passed the
158 * bounding-volume test for the viewing frustum, and the node's transform and
159 * state have already been applied to the indicated CullTraverserData object.
160 *
161 * The return value is true if this node should be visible, or false if it
162 * should be culled.
163 */
166 // For now, we update the character during the cull traversal; this prevents
167 // us from needlessly updating characters that aren't in the view frustum.
168 // We may need a better way to do this optimization later, to handle
169 // characters that might animate themselves in front of the view frustum.
170
171 if (_do_lod_animation) {
173
174 CPT(TransformState) rel_transform = get_rel_transform(trav, data);
175 LPoint3 center = _lod_center * rel_transform->get_mat();
176 PN_stdfloat dist2 = center.dot(center);
177
178 if (this_frame != _view_frame || dist2 < _view_distance2) {
179 _view_frame = this_frame;
180 _view_distance2 = dist2;
181
182 // Now compute the lod delay.
183 PN_stdfloat dist = sqrt(dist2);
184 double delay = 0.0;
185 if (dist > _lod_near_distance) {
186 delay = _lod_delay_factor * (dist - _lod_near_distance) / (_lod_far_distance - _lod_near_distance);
187 nassertr(delay > 0.0, false);
188 }
189 set_lod_current_delay(delay);
190
191 if (char_cat.is_spam()) {
192 char_cat.spam()
193 << "Distance to " << NodePath::any_path(this) << " in frame "
194 << this_frame << " is " << dist << ", computed delay is " << delay
195 << "\n";
196 }
197 }
198 }
199
200 update();
201 return true;
202}
203
204/**
205 * This is used to support NodePath::calc_tight_bounds(). It is not intended
206 * to be called directly, and it has nothing to do with the normal Panda
207 * bounding-volume computation.
208 *
209 * If the node contains any geometry, this updates min_point and max_point to
210 * enclose its bounding box. found_any is to be set true if the node has any
211 * geometry at all, or left alone if it has none. This method may be called
212 * over several nodes, so it may enter with min_point, max_point, and
213 * found_any already set.
214 *
215 * This function is recursive, and the return value is the transform after it
216 * has been modified by this node's transform.
217 */
218CPT(TransformState) Character::
219calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
220 const TransformState *transform, Thread *current_thread) const {
221 // This method is overridden by Character solely to provide a hook to force
222 // the joints to update before computing the bounding volume.
223 ((Character *)this)->update_to_now();
224
225 // Unfortunately, calling update_to_now() will invalidate the node's cached
226 // bounding volume, which causes a problem when this is called during the
227 // traversal, e.g. due to a ShowBoundsEffect. As a hacky fix to work
228 // around this, we will force-recompute all of the bounding volumes of our
229 // parent nodes immediately.
230 Parents parents = get_parents();
231 for (size_t i = 0; i < parents.get_num_parents(); ++i) {
232 PandaNode *parent = parents.get_parent(i);
233 parent->get_bounds();
234 }
235
236 return PandaNode::calc_tight_bounds(min_point, max_point,
237 found_any, transform, current_thread);
238}
239
240/**
241 * Merges old_bundle with new_bundle. old_bundle must be one of the
242 * PartBundles within this node. At the end of this call, the old_bundle
243 * pointer within this node will be replaced with the new_bundle pointer, and
244 * all geometry within this node will be updated to reference new_bundle.
245 *
246 * @deprecated Use the newer version of this method, below.
247 */
248void Character::
249merge_bundles(PartBundle *old_bundle, PartBundle *new_bundle) {
250 if (old_bundle == new_bundle) {
251 // Trivially return.
252 return;
253 }
254
255 // Find the PartBundleHandle of old_bundle.
256 PT(PartBundleHandle) old_bundle_handle;
257 Bundles::const_iterator bi;
258 for (bi = _bundles.begin(); bi != _bundles.end(); ++bi) {
259 if ((*bi)->get_bundle() == old_bundle) {
260 old_bundle_handle = (*bi);
261 break;
262 }
263 }
264 nassertv(!old_bundle_handle.is_null());
265
266 PT(PartBundleHandle) new_bundle_handle = new PartBundleHandle(new_bundle);
267 merge_bundles(old_bundle_handle, new_bundle_handle);
268}
269
270/**
271 * Merges old_bundle_handle->get_bundle() with new_bundle. old_bundle_handle
272 * must be one of the PartBundleHandle within this node. At the end of this
273 * call, the bundle pointer within the old_bundle_handle will be replaced with
274 * that within the new_bundle_handle pointer, and all geometry within this
275 * node will be updated to reference new_bundle.
276 *
277 * Normally, this is called when the two bundles have the same, or nearly the
278 * same, hierarchies. In this case, new_bundle will simply be assigned over
279 * the old_bundle position. However, if any joints are present in one bundle
280 * or the other, new_bundle will be modified to contain the union of all
281 * joints.
282 *
283 * The geometry below this node is also updated to reference new_bundle,
284 * instead of the original old_bundle.
285 *
286 * This method is intended to unify two different models that share a common
287 * skeleton, for instance, different LOD's of the same model.
288 */
289void Character::
290merge_bundles(PartBundleHandle *old_bundle_handle,
291 PartBundleHandle *new_bundle_handle) {
292 PartBundle *old_bundle = old_bundle_handle->get_bundle();
293 PartBundle *new_bundle = new_bundle_handle->get_bundle();
294 new_bundle->merge_anim_preloads(old_bundle);
295
296 update_bundle(old_bundle_handle, new_bundle);
297}
298
299/**
300 * Activates a special mode in which the character animates less frequently as
301 * it gets further from the camera. This is intended as a simple optimization
302 * to minimize the effort of computing animation for lots of characters that
303 * may not necessarily be very important to animate every frame.
304 *
305 * If the character is closer to the camera than near_distance, then it is
306 * animated its normal rate, every frame. If the character is exactly
307 * far_distance away, it is animated only every delay_factor seconds (which
308 * should be a number greater than 0). If the character is between
309 * near_distance and far_distance, its animation rate is linearly interpolated
310 * according to its distance between the two. The interpolation function
311 * continues beyond far_distance, so that the character is animated
312 * increasingly less frequently as it gets farther away.
313 *
314 * The distance calculations are made from center, which is a fixed point
315 * relative to the character node, to the camera's lod center or cull center
316 * node (or to the camera node itself).
317 *
318 * If multiple cameras are viewing the character in any given frame, the
319 * closest one counts.
320 */
322set_lod_animation(const LPoint3 &center,
323 PN_stdfloat far_distance, PN_stdfloat near_distance,
324 PN_stdfloat delay_factor) {
325 nassertv(far_distance >= near_distance);
326 nassertv(delay_factor >= 0.0f);
327 _lod_center = center;
328 _lod_far_distance = far_distance;
329 _lod_near_distance = near_distance;
330 _lod_delay_factor = delay_factor;
331 _do_lod_animation = (_lod_far_distance > _lod_near_distance && _lod_delay_factor > 0.0);
332 if (!_do_lod_animation) {
333 set_lod_current_delay(0.0);
334 }
335}
336
337/**
338 * Undoes the effect of a recent call to set_lod_animation(). Henceforth, the
339 * character will animate every frame, regardless of its distance from the
340 * camera.
341 */
344 _lod_center = LPoint3::zero();
345 _lod_far_distance = 0.0f;
346 _lod_near_distance = 0.0f;
347 _lod_delay_factor = 0.0f;
348 _do_lod_animation = false;
349 set_lod_current_delay(0.0);
350}
351
352/**
353 * Returns a pointer to the joint with the given name, if there is such a
354 * joint, or NULL if there is no such joint. This will not return a pointer
355 * to a slider.
356 */
358find_joint(const std::string &name) const {
359 int num_bundles = get_num_bundles();
360 for (int i = 0; i < num_bundles; ++i) {
361 PartGroup *part = get_bundle(i)->find_child(name);
362 if (part != nullptr && part->is_character_joint()) {
363 return DCAST(CharacterJoint, part);
364 }
365 }
366
367 return nullptr;
368}
369
370/**
371 * Returns a pointer to the slider with the given name, if there is such a
372 * slider, or NULL if there is no such slider. This will not return a pointer
373 * to a joint.
374 */
376find_slider(const std::string &name) const {
377 int num_bundles = get_num_bundles();
378 for (int i = 0; i < num_bundles; ++i) {
379 PartGroup *part = get_bundle(i)->find_child(name);
380 if (part != nullptr &&
381 part->is_of_type(CharacterSlider::get_class_type())) {
382 return DCAST(CharacterSlider, part);
383 }
384 }
385
386 return nullptr;
387}
388
389/**
390 * Writes a list of the Character's joints and sliders, in their hierchical
391 * structure, to the indicated output stream.
392 */
394write_parts(std::ostream &out) const {
395 int num_bundles = get_num_bundles();
396 for (int i = 0; i < num_bundles; ++i) {
397 get_bundle(i)->write(out, 0);
398 }
399}
400
401/**
402 * Writes a list of the Character's joints and sliders, along with each
403 * current position, in their hierchical structure, to the indicated output
404 * stream.
405 */
407write_part_values(std::ostream &out) const {
408 int num_bundles = get_num_bundles();
409 for (int i = 0; i < num_bundles; ++i) {
410 get_bundle(i)->write_with_value(out, 0);
411 }
412}
413
414/**
415 * Advances the character's frame to the current time, and then calls
416 * update(). This can be used by show code to force an update of the
417 * character's position to the current frame, regardless of whether the
418 * character is currently onscreen and animating.
419 *
420 * @deprecated Call update() instead.
421 */
424 update();
425}
426
427/**
428 * Recalculates the Character's joints and vertices for the current frame.
429 * Normally this is performed automatically during the render and need not be
430 * called explicitly.
431 */
433update() {
435 if (now != _last_auto_update) {
436 _last_auto_update = now;
437
438 if (char_cat.is_spam()) {
439 char_cat.spam()
440 << "Animating " << NodePath::any_path(this)
441 << " at time " << now << "\n";
442 }
443
444 PStatTimer timer(_joints_pcollector);
445 do_update();
446 }
447}
448
449/**
450 * Recalculates the character even if we think it doesn't need it.
451 */
453force_update() {
454 // Statistics
455 PStatTimer timer(_joints_pcollector);
456
457 // Update all the joints and sliders.
458 int num_bundles = get_num_bundles();
459 for (int i = 0; i < num_bundles; ++i) {
460 get_bundle(i)->force_update();
461 }
462}
463
464/**
465 * This is called by r_copy_subgraph(); the copy has already been made of this
466 * particular node (and this is the copy); this function's job is to copy all
467 * of the children from the original.
468 *
469 * Note that it includes the parameter inst_map, which is a map type, and is
470 * not (and cannot be) exported from PANDA.DLL. Thus, any derivative of
471 * PandaNode that is not also a member of PANDA.DLL *cannot* access this map,
472 * and probably should not even override this function.
473 */
474void Character::
475r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
476 Thread *current_thread) {
477 // We assume there will be no instancing going on below the Character node.
478 // If there is, too bad; it will get flattened out.
479
480 // We preempt the node's r_copy_children() operation with our own function
481 // that keeps track of the old vs. new nodes and also updates any Geoms we
482 // find with our new dynamic vertices.
483
484 const Character *from_char;
485 DCAST_INTO_V(from_char, from);
486 NodeMap node_map;
487 JointMap joint_map;
488
489 int num_bundles = get_num_bundles();
490 nassertv(from_char->get_num_bundles() == num_bundles);
491 int i;
492 for (i = 0; i < num_bundles; ++i) {
493 fill_joint_map(joint_map, get_bundle(i), from_char->get_bundle(i));
494 }
495
496 GeomVertexMap gvmap;
497 GeomJointMap gjmap;
498 GeomSliderMap gsmap;
499 r_copy_char(this, from_char, from_char, node_map, joint_map,
500 gvmap, gjmap, gsmap);
501
502 for (i = 0; i < num_bundles; ++i) {
503 copy_node_pointers(node_map, get_bundle(i), from_char->get_bundle(i));
504 }
505}
506
507/**
508 * Replaces the contents of the indicated PartBundleHandle (presumably stored
509 * within this node) with new_bundle.
510 */
511void Character::
512update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
513 if (old_bundle_handle->get_bundle() == new_bundle) {
514 // Trivially return.
515 return;
516 }
517
518 // First, merge the bundles, to ensure we have the same set of joints in the
519 // new bundle.
520 JointMap joint_map;
521 r_merge_bundles(joint_map, old_bundle_handle->get_bundle(), new_bundle);
522
523 PartBundleNode::update_bundle(old_bundle_handle, new_bundle);
524
525 // Now convert the geometry to use the new bundle.
526 GeomVertexMap gvmap;
527 GeomJointMap gjmap;
528 GeomSliderMap gsmap;
529 r_update_geom(this, joint_map, gvmap, gjmap, gsmap);
530}
531
532/**
533 * Returns the relative transform to convert from the LODNode space to the
534 * camera space.
535 */
536CPT(TransformState) Character::
537get_rel_transform(CullTraverser *trav, CullTraverserData &data) {
538 // Get a pointer to the camera node.
539 Camera *camera = trav->get_scene()->get_camera_node();
540
541 // Get the camera space transform.
542 CPT(TransformState) rel_transform;
543
544 NodePath lod_center = camera->get_lod_center();
545 if (!lod_center.is_empty()) {
546 rel_transform =
547 lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
548 } else {
549 NodePath cull_center = camera->get_cull_center();
550 if (!cull_center.is_empty()) {
551 rel_transform =
552 cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
553 } else {
554 rel_transform = data.get_modelview_transform(trav);
555 }
556 }
557
558 return rel_transform;
559}
560
561/**
562 * The actual implementation of update(). Assumes the appropriate
563 * PStatCollector has already been started.
564 */
565void Character::
566do_update() {
567 // Update all the joints and sliders.
568 if (even_animation) {
569 int num_bundles = get_num_bundles();
570 for (int i = 0; i < num_bundles; ++i) {
571 get_bundle(i)->force_update();
572 }
573 } else {
574 int num_bundles = get_num_bundles();
575 for (int i = 0; i < num_bundles; ++i) {
576 get_bundle(i)->update();
577 }
578 }
579}
580
581/**
582 * Changes the amount of delay we should impose due to the LOD animation
583 * setting.
584 */
585void Character::
586set_lod_current_delay(double delay) {
587 int num_bundles = get_num_bundles();
588 for (int i = 0; i < num_bundles; ++i) {
589 get_bundle(i)->set_update_delay(delay);
590 }
591}
592
593/**
594 * After the joint hierarchy has already been copied from the indicated
595 * hierarchy, this recursively walks through the joints and builds up a
596 * mapping from old to new.
597 */
598void Character::
599fill_joint_map(Character::JointMap &joint_map,
600 PartGroup *copy, PartGroup *orig) {
601 joint_map[orig] = copy;
602
603 int i = 0, j = 0;
604 int copy_num_children = copy->get_num_children();
605 int orig_num_children = orig->get_num_children();
606
607 while (i < copy_num_children && j < orig_num_children) {
608 PartGroup *pc = copy->get_child(i);
609 PartGroup *ac = orig->get_child(j);
610
611 if (pc->get_name() < ac->get_name()) {
612 i++;
613 } else if (ac->get_name() < pc->get_name()) {
614 j++;
615 } else {
616 fill_joint_map(joint_map, pc, ac);
617 i++;
618 j++;
619 }
620 }
621}
622
623/**
624 * Recursively checks the two bundles for a matching hierarchy, and adds nodes
625 * as necessary to "new_group" where they are not already present. Also fills
626 * joint_map in the same manner as fill_joint_map().
627 */
628void Character::
629r_merge_bundles(Character::JointMap &joint_map,
630 PartGroup *old_group, PartGroup *new_group) {
631 joint_map[old_group] = new_group;
632
633 if (new_group->is_character_joint()) {
634 CharacterJoint *new_joint;
635 DCAST_INTO_V(new_joint, new_group);
636
637 // Make sure the new_joint references this as its new character.
638 new_joint->_character = this;
639
640 if (old_group != new_group &&
641 old_group->is_character_joint()) {
642 CharacterJoint *old_joint;
643 DCAST_INTO_V(old_joint, old_group);
644
645 // Since the old_joint will be getting dropped, reset its character
646 // reference.
647 old_joint->_character = nullptr;
648
649 // Copy any _net_transform and _local_transform operations to the new
650 // joint.
651 CharacterJoint::NodeList::iterator ni;
652 for (ni = old_joint->_net_transform_nodes.begin();
653 ni != old_joint->_net_transform_nodes.end();
654 ++ni) {
655 new_joint->_net_transform_nodes.insert(*ni);
656 }
657 for (ni = old_joint->_local_transform_nodes.begin();
658 ni != old_joint->_local_transform_nodes.end();
659 ++ni) {
660 new_joint->_local_transform_nodes.insert(*ni);
661 }
662 }
663 }
664
665 if (old_group == new_group) {
666 return;
667 }
668
669 int i = 0, j = 0;
670 int old_num_children = old_group->get_num_children();
671 int new_num_children = new_group->get_num_children();
672
673 PartGroup::Children new_children(PartGroup::get_class_type());
674 new_children.reserve(std::max(old_num_children, new_num_children));
675
676 while (i < old_num_children && j < new_num_children) {
677 PartGroup *pc = old_group->get_child(i);
678 PartGroup *ac = new_group->get_child(j);
679
680 if (pc->get_name() < ac->get_name()) {
681 // Here is a group that exists in old_group, but not in new_group.
682 // Duplicate it.
683 PartGroup *new_pc = pc->make_copy();
684 new_children.push_back(new_pc);
685
686 r_merge_bundles(joint_map, pc, new_pc);
687 i++;
688
689 } else if (ac->get_name() < pc->get_name()) {
690 // Here is a group that exists in new_group, but not in old_group.
691 // Preserve it.
692 new_children.push_back(ac);
693
694 r_merge_bundles(joint_map, ac, ac);
695 j++;
696
697 } else {
698 // Here is a group that exists in both. Preserve it.
699 new_children.push_back(ac);
700
701 r_merge_bundles(joint_map, pc, ac);
702 i++;
703 j++;
704 }
705 }
706
707 while (i < old_num_children) {
708 PartGroup *pc = old_group->get_child(i);
709
710 // Here is a group that exists in old_group, but not in new_group.
711 // Duplicate it.
712 PartGroup *new_pc = pc->make_copy();
713 new_children.push_back(new_pc);
714
715 r_merge_bundles(joint_map, pc, new_pc);
716 i++;
717 }
718
719 while (j < new_num_children) {
720 PartGroup *ac = new_group->get_child(j);
721
722 // Here is a group that exists in new_group, but not in old_group.
723 // Preserve it.
724 new_children.push_back(ac);
725
726 r_merge_bundles(joint_map, ac, ac);
727 j++;
728 }
729
730 new_group->_children.swap(new_children);
731}
732
733
734/**
735 * Recursively walks the scene graph hierarchy below the Character node,
736 * duplicating it while noting the orig:copy node mappings, and also updates
737 * any GeomNodes found.
738 */
739void Character::
740r_copy_char(PandaNode *dest, const PandaNode *source,
741 const Character *from, Character::NodeMap &node_map,
742 const Character::JointMap &joint_map,
745
746 if (source->is_geom_node()) {
747 const GeomNode *source_gnode;
748 GeomNode *dest_gnode;
749 DCAST_INTO_V(source_gnode, source);
750 DCAST_INTO_V(dest_gnode, dest);
751
752 dest_gnode->remove_all_geoms();
753 int num_geoms = source_gnode->get_num_geoms();
754 for (int i = 0; i < num_geoms; i++) {
755 const Geom *geom = source_gnode->get_geom(i);
756 const RenderState *state = source_gnode->get_geom_state(i);
757 dest_gnode->add_geom(copy_geom(geom, joint_map, gvmap, gjmap, gsmap), state);
758 }
759 }
760
761 int num_children = source->get_num_children();
762 for (int i = 0; i < num_children; i++) {
763 const PandaNode *source_child = source->get_child(i);
764 int source_sort = source->get_child_sort(i);
765
766 PT(PandaNode) dest_child;
767 if (source_child->is_of_type(Character::get_class_type())) {
768 // We make a special case for nodes of type Character. If we encounter
769 // one of these, we have a Character under a Character, and the nested
770 // Character's copy should be called instead of ours.
771 dest_child = source_child->copy_subgraph();
772
773 } else {
774 // Otherwise, we assume that make_copy() will make a suitable copy of
775 // the node. This does limit the sorts of things we can have parented
776 // to a Character and expect copy_subgraph() to work correctly. Too
777 // bad.
778 dest_child = source_child->make_copy();
779 r_copy_char(dest_child, source_child, from, node_map, joint_map,
780 gvmap, gjmap, gsmap);
781 }
782 dest->add_child(dest_child, source_sort);
783 node_map[source_child] = dest_child;
784 }
785}
786
787/**
788 * Walks the hierarchy, updating any GeomNodes in-place to reference the new
789 * animation tables within this Character.
790 */
791void Character::
792r_update_geom(PandaNode *node, const Character::JointMap &joint_map,
796 if (node->is_geom_node()) {
797 GeomNode *gnode;
798 DCAST_INTO_V(gnode, node);
799
800 int num_geoms = gnode->get_num_geoms();
801 for (int i = 0; i < num_geoms; i++) {
802 CPT(Geom) geom = gnode->get_geom(i);
803 PT(Geom) new_geom = copy_geom(geom, joint_map, gvmap, gjmap, gsmap);
804 gnode->set_geom(i, new_geom);
805 }
806 }
807
808 int num_children = node->get_num_children();
809 for (int i = 0; i < num_children; i++) {
810 PandaNode *child = node->get_child(i);
811
812 r_update_geom(child, joint_map, gvmap, gjmap, gsmap);
813 }
814}
815
816/**
817 * Makes a new copy of the Geom with the dynamic vertex arrays replaced to
818 * reference this Character instead of the other one. If no arrays have
819 * changed, simply returns the same Geom.
820 */
821PT(Geom) Character::
822copy_geom(const Geom *source, const Character::JointMap &joint_map,
825 CPT(GeomVertexFormat) format = source->get_vertex_data()->get_format();
826 if (format->get_animation().get_animation_type() == Geom::AT_none) {
827 // Not animated, so never mind.
828 return (Geom *)source;
829 }
830
831 PT(Geom) dest = source->make_copy();
832
833 CPT(GeomVertexData) orig_vdata = source->get_vertex_data();
834 PT(GeomVertexData) new_vdata;
835 GeomVertexMap::iterator gvmi = gvmap.find(orig_vdata);
836 if (gvmi != gvmap.end()) {
837 new_vdata = (*gvmi).second;
838 } else {
839 new_vdata = new GeomVertexData(*orig_vdata);
840
841 new_vdata->set_transform_table(redirect_transform_table(orig_vdata->get_transform_table(), joint_map, gjmap));
842 new_vdata->set_transform_blend_table(redirect_transform_blend_table(orig_vdata->get_transform_blend_table(), joint_map, gjmap));
843 new_vdata->set_slider_table(redirect_slider_table(orig_vdata->get_slider_table(), gsmap));
844
845 gvmap.insert(GeomVertexMap::value_type(orig_vdata, new_vdata));
846 }
847
848 dest->set_vertex_data(new_vdata);
849
850 return dest;
851}
852
853/**
854 * Creates _net_transform_nodes and _local_transform_nodes as appropriate in
855 * each of the Character's joints, as copied from the other Character.
856 */
857void Character::
858copy_node_pointers(const Character::NodeMap &node_map,
859 PartGroup *dest, const PartGroup *source) {
860 if (dest->is_character_joint()) {
861 nassertv(dest != source);
862 const CharacterJoint *source_joint;
863 CharacterJoint *dest_joint;
864 DCAST_INTO_V(source_joint, source);
865 DCAST_INTO_V(dest_joint, dest);
866
867 CharacterJoint::NodeList::const_iterator ai;
868 for (ai = source_joint->_net_transform_nodes.begin();
869 ai != source_joint->_net_transform_nodes.end();
870 ++ai) {
871 PandaNode *source_node = (*ai);
872
873 NodeMap::const_iterator mi;
874 mi = node_map.find(source_node);
875 if (mi != node_map.end()) {
876 PandaNode *dest_node = (*mi).second;
877
878 // Here's an internal joint that the source Character was animating
879 // directly. We'll animate our corresponding joint the same way.
880 dest_joint->set_character(this);
881 dest_joint->add_net_transform(dest_node);
882 }
883 }
884
885 for (ai = source_joint->_local_transform_nodes.begin();
886 ai != source_joint->_local_transform_nodes.end();
887 ++ai) {
888 PandaNode *source_node = (*ai);
889
890 NodeMap::const_iterator mi;
891 mi = node_map.find(source_node);
892 if (mi != node_map.end()) {
893 PandaNode *dest_node = (*mi).second;
894
895 // Here's an internal joint that the source Character was animating
896 // directly. We'll animate our corresponding joint the same way.
897 dest_joint->set_character(this);
898 dest_joint->add_local_transform(dest_node);
899 }
900 }
901 }
902
903 // Now recurse over children.
904 int i = 0, j = 0;
905 int dest_num_children = dest->get_num_children();
906 int source_num_children = source->get_num_children();
907
908 while (i < dest_num_children && j < source_num_children) {
909 PartGroup *pc = dest->get_child(i);
910 PartGroup *ac = source->get_child(j);
911
912 if (pc->get_name() < ac->get_name()) {
913 i++;
914 } else if (ac->get_name() < pc->get_name()) {
915 j++;
916 } else {
917 copy_node_pointers(node_map, pc, ac);
918 i++;
919 j++;
920 }
921 }
922}
923
924/**
925 * Creates a new TransformTable, similar to the indicated one, with the joint
926 * and slider pointers redirected into this object.
927 */
928CPT(TransformTable) Character::
929redirect_transform_table(const TransformTable *source,
930 const Character::JointMap &joint_map,
932 if (source == nullptr) {
933 return nullptr;
934 }
935
936 PT(TransformTable) dest = new TransformTable(*source);
937
938 int num_transforms = dest->get_num_transforms();
939 for (int i = 0; i < num_transforms; ++i) {
940 const VertexTransform *vt = dest->get_transform(i);
941 PT(JointVertexTransform) new_jvt = redirect_joint(vt, joint_map, gjmap);
942 if (new_jvt != nullptr) {
943 dest->set_transform(i, new_jvt);
944 }
945 }
946
947 return TransformTable::register_table(dest);
948}
949
950/**
951 * Creates a new TransformBlendTable, similar to the indicated one, with the
952 * joint and slider pointers redirected into this object.
953 */
954CPT(TransformBlendTable) Character::
955redirect_transform_blend_table(const TransformBlendTable *source,
956 const Character::JointMap &joint_map,
958 if (source == nullptr) {
959 return nullptr;
960 }
961
962 PT(TransformBlendTable) dest = new TransformBlendTable(*source);
963
964 int num_blends = dest->get_num_blends();
965 for (int i = 0; i < num_blends; ++i) {
966 TransformBlend blend = dest->get_blend(i);
967 int num_transforms = blend.get_num_transforms();
968 for (int j = 0; j < num_transforms; ++j) {
969 const VertexTransform *vt = blend.get_transform(j);
970 PT(JointVertexTransform) new_jvt = redirect_joint(vt, joint_map, gjmap);
971 if (new_jvt != nullptr) {
972 blend.set_transform(j, new_jvt);
973 }
974 }
975 dest->set_blend(i, blend);
976 }
977
978 return dest;
979}
980
981/**
982 * Creates a new SliderTable, similar to the indicated one, with the joint and
983 * slider pointers redirected into this object.
984 */
985CPT(SliderTable) Character::
986redirect_slider_table(const SliderTable *source,
988 if (source == nullptr) {
989 return nullptr;
990 }
991
992 PT(SliderTable) dest = new SliderTable(*source);
993
994 int num_sliders = dest->get_num_sliders();
995 for (int i = 0; i < num_sliders; ++i) {
996 const VertexSlider *vs = dest->get_slider(i);
997 PT(CharacterVertexSlider) new_cvs = redirect_slider(vs, gsmap);
998 if (new_cvs != nullptr) {
999 dest->set_slider(i, new_cvs);
1000 }
1001 }
1002
1003 return SliderTable::register_table(dest);
1004}
1005
1006/**
1007 * Creates a new JointVertexTransform that is similar to the indicated one,
1008 * but points into this character. If one was already created (in the
1009 * GeomJointMap), returns it instead.
1010 */
1012redirect_joint(const VertexTransform *vt,
1013 const Character::JointMap &joint_map,
1014 Character::GeomJointMap &gjmap) {
1015 GeomJointMap::iterator ji;
1016 ji = gjmap.find(vt);
1017 if (ji != gjmap.end()) {
1018 return (*ji).second;
1019 }
1020
1021 PT(JointVertexTransform) new_jvt;
1022
1023 if (vt->is_of_type(JointVertexTransform::get_class_type())) {
1024 const JointVertexTransform *jvt = DCAST(JointVertexTransform, vt);
1025 const CharacterJoint *orig_joint = jvt->get_joint();
1026 JointMap::const_iterator jmi = joint_map.find(orig_joint);
1027 if (jmi == joint_map.end()) {
1028 char_cat.error()
1029 << "Could not find joint " << *orig_joint
1030 << " within the character hierarchy.\n";
1031
1032 } else {
1033 CharacterJoint *joint = DCAST(CharacterJoint, (*jmi).second);
1034 new_jvt = new JointVertexTransform(joint);
1035 }
1036 }
1037
1038 gjmap[vt] = new_jvt;
1039 return new_jvt;
1040}
1041
1042/**
1043 * Creates a new CharacterVertexSlider that is similar to the indicated one,
1044 * but points into this character. If one was already created (in the
1045 * GeomSliderMap), returns it instead.
1046 */
1048redirect_slider(const VertexSlider *vs, Character::GeomSliderMap &gsmap) {
1049 GeomSliderMap::iterator ji;
1050 ji = gsmap.find(vs);
1051 if (ji != gsmap.end()) {
1052 return (*ji).second;
1053 }
1054
1055 PT(CharacterVertexSlider) new_cvs;
1056
1057 if (vs->is_of_type(CharacterVertexSlider::get_class_type())) {
1058 const CharacterVertexSlider *cvs = DCAST(CharacterVertexSlider, vs);
1059 CharacterSlider *slider = find_slider(cvs->get_char_slider()->get_name());
1060 if (slider != nullptr) {
1061 new_cvs = new CharacterVertexSlider(slider);
1062 }
1063 }
1064
1065 gsmap[vs] = new_cvs;
1066 return new_cvs;
1067}
1068
1069/**
1070 * Recursively walks through the joint hierarchy and clears any _character
1071 * pointers on all the joints. Intended to be called just before Character
1072 * destruction.
1073 */
1074void Character::
1075r_clear_joint_characters(PartGroup *part) {
1076 if (part->is_character_joint()) {
1077 CharacterJoint *joint = DCAST(CharacterJoint, part);
1078
1079 // It is possible for the joint to reference a different Character here--
1080 // after merge_bundles() has been called, a particular joint will be
1081 // listed within more than one Character node, but it can only point back
1082 // to one of them.
1083 if (joint->get_character() == this) {
1084 joint->set_character(nullptr);
1085 }
1086 }
1087
1088 int num_children = part->get_num_children();
1089 for (int i = 0; i < num_children; ++i) {
1090 PartGroup *child = part->get_child(i);
1091 r_clear_joint_characters(child);
1092 }
1093}
1094
1095/**
1096 * Tells the BamReader how to create objects of type Character.
1097 */
1100 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
1101}
1102
1103/**
1104 * Writes the contents of this object to the datagram for shipping out to a
1105 * Bam file.
1106 */
1108write_datagram(BamWriter *manager, Datagram &dg) {
1109 PartBundleNode::write_datagram(manager, dg);
1110
1111 // Record 0 parts written--we no longer write an array of parts.
1112 dg.add_uint16(0);
1113}
1114
1115/**
1116 * Receives an array of pointers, one for each time manager->read_pointer()
1117 * was called in fillin(). Returns the number of pointers processed.
1118 */
1120complete_pointers(TypedWritable **p_list, BamReader *manager) {
1121 // Pretend to read the _temp_num_parts parts that were found in the bam
1122 // file.
1123 return PartBundleNode::complete_pointers(p_list, manager) + _temp_num_parts;
1124}
1125
1126/**
1127 * This function is called by the BamReader's factory when a new object of
1128 * type Character is encountered in the Bam file. It should create the
1129 * Character and extract its information from the file.
1130 */
1131TypedWritable *Character::
1132make_from_bam(const FactoryParams &params) {
1133 Character *node = new Character("");
1134 DatagramIterator scan;
1135 BamReader *manager;
1136
1137 parse_params(params, scan, manager);
1138 node->fillin(scan, manager);
1139
1140 return node;
1141}
1142
1143/**
1144 * This internal function is called by make_from_bam to read in all of the
1145 * relevant data from the BamFile for the new Character.
1146 */
1147void Character::
1148fillin(DatagramIterator &scan, BamReader *manager) {
1149 PartBundleNode::fillin(scan, manager);
1150
1151 // We no longer read an array of parts here, but for backward compatibility,
1152 // we must read in the number of parts that used to be there, and read past
1153 // each of the pointers.
1154 _temp_num_parts = scan.get_uint16();
1155 for (unsigned int i = 0; i < _temp_num_parts; i++) {
1156 manager->read_pointer(scan);
1157 }
1158
1159#ifdef DO_PSTATS
1160 // Reinitialize our collectors with our name, now that we know it.
1161 if (has_name()) {
1162 _joints_pcollector =
1163 PStatCollector(PStatCollector(_animation_pcollector, get_name()), "Joints");
1164 _skinning_pcollector =
1165 PStatCollector(PStatCollector(_animation_pcollector, get_name()), "Vertices");
1166 }
1167#endif
1168}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition bamReader.I:275
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 the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition bamReader.h:110
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition camera.h:35
get_lod_center
Returns the point from which the LOD distances will be measured, if it was set by set_lod_center(),...
Definition camera.h:75
get_cull_center
Returns the point from which the culling operations will be performed, if it was set by set_cull_cent...
Definition camera.h:67
The collection of all the joints and sliders in the character.
This represents one joint of the character's animation, containing an animating transform matrix.
bool add_net_transform(PandaNode *node)
Adds the indicated node to the list of nodes that will be updated each frame with the joint's net tra...
bool add_local_transform(PandaNode *node)
Adds the indicated node to the list of nodes that will be updated each frame with the joint's local t...
This is a morph slider within the character.
This is a specialization on VertexSlider that returns the slider value associated with a particular C...
const CharacterSlider * get_char_slider() const
Returns the CharacterSlider object for which this object returns the slider value.
An animated character, with skeleton-morph animation and either soft- skinned or hard-skinned vertice...
Definition character.h:38
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
void write_part_values(std::ostream &out) const
Writes a list of the Character's joints and sliders, along with each current position,...
void set_lod_animation(const LPoint3 &center, PN_stdfloat far_distance, PN_stdfloat near_distance, PN_stdfloat delay_factor)
Activates a special mode in which the character animates less frequently as it gets further from the ...
virtual PandaNode * dupe_for_flatten() const
This is similar to make_copy(), but it makes a copy for the specific purpose of flatten.
void write_parts(std::ostream &out) const
Writes a list of the Character's joints and sliders, in their hierchical structure,...
CharacterSlider * find_slider(const std::string &name) const
Returns a pointer to the slider with the given name, if there is such a slider, or NULL if there is n...
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
void force_update()
Recalculates the character even if we think it doesn't need it.
virtual PandaNode * make_copy() const
The Character make_copy() function will make a new copy of the Character, with all of its joints copi...
void update_to_now()
Advances the character's frame to the current time, and then calls update().
void clear_lod_animation()
Undoes the effect of a recent call to set_lod_animation().
void update()
Recalculates the Character's joints and vertices for the current frame.
virtual PandaNode * combine_with(PandaNode *other)
Collapses this node with the other node, if possible, and returns a pointer to the combined node,...
static void register_with_read_factory()
Tells the BamReader how to create objects of type Character.
CharacterJoint * find_joint(const std::string &name) const
Returns a pointer to the joint with the given name, if there is such a joint, or NULL if there is no ...
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition clockObject.h:91
get_frame_count
Returns the number of times tick() has been called since the ClockObject was created,...
Definition clockObject.h:94
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
SceneSetup * get_scene() const
Returns the SceneSetup object.
A class to retrieve the individual data elements previously stored in a Datagram.
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition datagram.I:85
An instance of this class is passed to the Factory when requesting it to do its business and construc...
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition factory.I:73
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition geomNode.cxx:612
get_num_geoms
Returns the number of geoms in the node.
Definition geomNode.h:71
void remove_all_geoms()
Removes all the geoms from the node at once.
Definition geomNode.I:126
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition geomNode.h:75
void set_geom(int n, Geom *geom)
Replaces the nth Geom of the node with a new pointer.
Definition geomNode.cxx:663
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
A container for geometry primitives.
Definition geom.h:54
virtual Geom * make_copy() const
Returns a newly-allocated Geom that is a shallow copy of this one.
Definition geom.cxx:100
This is a specialization on VertexTransform that returns the transform necessary to move vertices as ...
const CharacterJoint * get_joint() const
Returns the joint for which this object returns the transform.
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition namable.I:44
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
static NodePath any_path(PandaNode *node, Thread *current_thread=Thread::get_current_thread())
Returns a new NodePath that represents any arbitrary path from the root to the indicated node.
Definition nodePath.I:62
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition nodePath.I:188
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition pStatTimer.h:30
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
virtual bool is_geom_node() const
A simple downcast check.
virtual PandaNode * combine_with(PandaNode *other)
Collapses this PandaNode with the other PandaNode, if possible, and returns a pointer to the combined...
get_parents
Returns an object that can be used to walk through the list of parents of the node,...
Definition pandaNode.h:784
get_child
Returns the nth child node of this node.
Definition pandaNode.h:124
virtual PandaNode * make_copy() const
Returns a newly-allocated PandaNode that is a shallow copy of this one.
int get_child_sort(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the sort index of the nth child node of this node (that is, the number that was passed to add...
Definition pandaNode.I:78
get_parent
Returns the nth parent node of this node.
Definition pandaNode.h:118
get_num_children
Returns the number of child nodes this node has.
Definition pandaNode.h:124
This is a trivial class returned by PartBundleNode::get_bundle().
get_bundle
Returns the actual PartBundle embedded within the handle.
This is a node that contains a pointer to an PartBundle.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
virtual void write_datagram(BamWriter *manager, Datagram &me)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This is the root of a MovingPart hierarchy.
Definition partBundle.h:46
void set_update_delay(double delay)
Specifies the minimum amount of time, in seconds, that should elapse between any two consecutive upda...
Definition partBundle.I:208
bool update()
Updates all the parts in the bundle to reflect the data for the current frame (as set in each of the ...
void merge_anim_preloads(const PartBundle *other)
Copies the contents of the other PartBundle's preload table into this one.
bool force_update()
Updates all the parts in the bundle to reflect the data for the current frame, whether we believe it ...
virtual void write(std::ostream &out, int indent_level) const
Writes a brief description of the bundle and all of its descendants.
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
PartGroup * copy_subgraph() const
Allocates and returns a new copy of this node and of all of its children.
Definition partGroup.cxx:75
virtual PartGroup * make_copy() const
Allocates and returns a new copy of the node.
Definition partGroup.cxx:67
PartGroup * find_child(const std::string &name) const
Returns the first descendant found with the indicated name, or NULL if no such descendant exists.
get_child
Returns the nth child of the group.
Definition partGroup.h:72
virtual bool is_character_joint() const
Returns true if this part is a CharacterJoint, false otherwise.
Definition partGroup.cxx:58
virtual void write_with_value(std::ostream &out, int indent_level) const
Writes a brief description of the group, showing its current value, and that of all of its descendant...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
Camera * get_camera_node() const
Returns the camera used to render the scene.
Definition sceneSetup.I:115
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition sliderTable.h:37
A thread; that is, a lightweight process.
Definition thread.h:46
This structure collects together the different combinations of transforms and blend amounts used by a...
This defines a single entry in a TransformBlendTable.
get_num_transforms
Returns the number of transforms stored in the blend object.
set_transform
Replaces the nth transform stored in the blend object.
get_transform
Returns the nth transform stored in the blend object.
Indicates a coordinate-system transform on vertices.
Stores the total set of VertexTransforms that the vertices in a particular GeomVertexData object migh...
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition typedObject.I:38
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
Base class for objects that can be written to and read from Bam files.
This is an abstract base class that retains some slider value, which is a linear value that typically...
This is an abstract base class that holds a pointer to some transform, computed in some arbitrary way...
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
This is our own Panda specialization on the default STL map.
Definition pmap.h:49
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.