瀏覽代碼

WIP implementation of #1690

rdb 1 年之前
父節點
當前提交
1c41ebe05b
共有 100 個文件被更改,包括 984 次插入867 次删除
  1. 3 3
      contrib/src/rplight/pssmCameraRig.cxx
  2. 4 4
      direct/src/interval/cConstrainTransformInterval.cxx
  3. 22 19
      direct/src/interval/cLerpNodePathInterval.cxx
  4. 17 11
      panda/src/bullet/bulletBodyNode.cxx
  5. 14 19
      panda/src/bullet/bulletCharacterControllerNode.cxx
  6. 1 1
      panda/src/bullet/bulletCharacterControllerNode.h
  7. 6 9
      panda/src/bullet/bulletSoftBodyNode.cxx
  8. 1 1
      panda/src/bullet/bulletSoftBodyNode.h
  9. 3 3
      panda/src/bullet/bulletVehicle.cxx
  10. 37 14
      panda/src/bullet/bullet_utils.cxx
  11. 2 0
      panda/src/bullet/bullet_utils.h
  12. 1 1
      panda/src/chan/animChannelMatrixDynamic.I
  13. 22 20
      panda/src/chan/animChannelMatrixDynamic.cxx
  14. 4 4
      panda/src/chan/animChannelMatrixDynamic.h
  15. 1 1
      panda/src/chan/animChannelScalarDynamic.I
  16. 7 5
      panda/src/chan/animChannelScalarDynamic.cxx
  17. 2 2
      panda/src/chan/animChannelScalarDynamic.h
  18. 5 1
      panda/src/chan/partBundleNode.cxx
  19. 8 8
      panda/src/char/character.cxx
  20. 3 4
      panda/src/char/character.h
  21. 5 5
      panda/src/char/characterJoint.cxx
  22. 3 3
      panda/src/char/characterJointEffect.cxx
  23. 3 3
      panda/src/char/characterJointEffect.h
  24. 11 12
      panda/src/collide/collisionBox.cxx
  25. 1 1
      panda/src/collide/collisionBox.h
  26. 7 11
      panda/src/collide/collisionCapsule.cxx
  27. 9 9
      panda/src/collide/collisionEntry.I
  28. 12 19
      panda/src/collide/collisionEntry.cxx
  29. 6 6
      panda/src/collide/collisionEntry.h
  30. 4 3
      panda/src/collide/collisionHandlerFloor.cxx
  31. 8 9
      panda/src/collide/collisionHandlerFluidPusher.cxx
  32. 6 5
      panda/src/collide/collisionHandlerGravity.cxx
  33. 5 4
      panda/src/collide/collisionHandlerPusher.cxx
  34. 3 4
      panda/src/collide/collisionHeightfield.cxx
  35. 4 10
      panda/src/collide/collisionLevelState.I
  36. 2 2
      panda/src/collide/collisionLevelStateBase.cxx
  37. 3 3
      panda/src/collide/collisionNode.cxx
  38. 28 32
      panda/src/collide/collisionPolygon.cxx
  39. 1 1
      panda/src/collide/collisionPolygon.h
  40. 7 10
      panda/src/collide/collisionSphere.cxx
  41. 3 3
      panda/src/collide/collisionVisualizer.cxx
  42. 1 2
      panda/src/cull/cullBinBackToFront.cxx
  43. 1 2
      panda/src/cull/cullBinFrontToBack.cxx
  44. 1 5
      panda/src/cull/cullBinStateSorted.I
  45. 14 14
      panda/src/display/graphicsEngine.cxx
  46. 4 4
      panda/src/display/graphicsStateGuardian.I
  47. 111 105
      panda/src/display/graphicsStateGuardian.cxx
  48. 12 12
      panda/src/display/graphicsStateGuardian.h
  49. 9 9
      panda/src/distort/projectionScreen.cxx
  50. 14 16
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  51. 2 2
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  52. 2 2
      panda/src/egg2pg/characterMaker.cxx
  53. 126 13
      panda/src/egg2pg/eggLoader.cxx
  54. 2 1
      panda/src/egg2pg/eggLoader.h
  55. 1 1
      panda/src/egg2pg/eggRenderState.cxx
  56. 18 8
      panda/src/egg2pg/eggSaver.cxx
  57. 2 2
      panda/src/framework/windowFramework.cxx
  58. 5 5
      panda/src/glstuff/glCgShaderContext_src.cxx
  59. 6 6
      panda/src/glstuff/glCgShaderContext_src.h
  60. 40 43
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  61. 2 2
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  62. 5 5
      panda/src/glstuff/glShaderContext_src.cxx
  63. 6 6
      panda/src/glstuff/glShaderContext_src.h
  64. 6 3
      panda/src/gobj/shaderContext.h
  65. 2 2
      panda/src/grutil/cardMaker.cxx
  66. 2 2
      panda/src/grutil/frameRateMeter.cxx
  67. 1 1
      panda/src/grutil/frameRateMeter.h
  68. 1 1
      panda/src/grutil/multitexReducer.I
  69. 5 5
      panda/src/grutil/multitexReducer.cxx
  70. 3 5
      panda/src/grutil/multitexReducer.h
  71. 2 2
      panda/src/grutil/nodeVertexTransform.cxx
  72. 35 37
      panda/src/grutil/pipeOcclusionCullTraverser.cxx
  73. 7 7
      panda/src/grutil/pipeOcclusionCullTraverser.h
  74. 2 1
      panda/src/grutil/rigidBodyCombiner.cxx
  75. 2 2
      panda/src/grutil/sceneGraphAnalyzerMeter.cxx
  76. 1 1
      panda/src/grutil/sceneGraphAnalyzerMeter.h
  77. 10 11
      panda/src/grutil/shaderTerrainMesh.cxx
  78. 2 3
      panda/src/grutil/shaderTerrainMesh.h
  79. 2 2
      panda/src/gsgbase/graphicsStateGuardianBase.h
  80. 4 6
      panda/src/parametrics/nurbsSurfaceEvaluator.cxx
  81. 5 5
      panda/src/parametrics/ropeNode.cxx
  82. 2 2
      panda/src/particlesystem/geomParticleRenderer.cxx
  83. 8 11
      panda/src/particlesystem/particleSystem.cxx
  84. 14 8
      panda/src/pgraph/accumulatedAttribs.cxx
  85. 3 2
      panda/src/pgraph/accumulatedAttribs.h
  86. 31 36
      panda/src/pgraph/billboardEffect.cxx
  87. 7 7
      panda/src/pgraph/billboardEffect.h
  88. 38 36
      panda/src/pgraph/compassEffect.cxx
  89. 3 3
      panda/src/pgraph/compassEffect.h
  90. 2 2
      panda/src/pgraph/cullBin.h
  91. 1 1
      panda/src/pgraph/cullHandler.cxx
  92. 34 28
      panda/src/pgraph/cullPlanes.cxx
  93. 3 2
      panda/src/pgraph/cullResult.cxx
  94. 0 1
      panda/src/pgraph/cullResult.h
  95. 3 3
      panda/src/pgraph/cullTraverser.I
  96. 11 12
      panda/src/pgraph/cullTraverser.cxx
  97. 6 6
      panda/src/pgraph/cullTraverser.h
  98. 11 11
      panda/src/pgraph/cullTraverserData.I
  99. 27 40
      panda/src/pgraph/cullTraverserData.cxx
  100. 10 10
      panda/src/pgraph/cullTraverserData.h

+ 3 - 3
contrib/src/rplight/pssmCameraRig.cxx

@@ -119,7 +119,7 @@ void PSSMCameraRig::reparent_to(NodePath parent) {
  * @return view-projection matrix of the split
  */
 LMatrix4 PSSMCameraRig::compute_mvp(size_t split_index) {
-  LMatrix4 transform = _parent.get_transform(_cam_nodes[split_index])->get_mat();
+  LMatrix4 transform = _parent.get_transform(_cam_nodes[split_index]).get_mat();
   return transform * _cameras[split_index]->get_lens()->get_projection_mat();
 }
 
@@ -308,7 +308,7 @@ void PSSMCameraRig::compute_pssm_splits(const LMatrix4& transform, float max_dis
     LVecBase3 best_min_extent, best_max_extent;
 
     // Find minimum and maximum extents of the points
-    LMatrix4 merged_transform = _parent.get_transform(_cam_nodes[i])->get_mat();
+    LMatrix4 merged_transform = _parent.get_transform(_cam_nodes[i]).get_mat();
     find_min_max_extents(best_min_extent, best_max_extent, merged_transform, proj_points, cam);
 
     // Find the film size to cover all points
@@ -367,7 +367,7 @@ void PSSMCameraRig::update(NodePath cam_node, const LVecBase3 &light_vector) {
   _update_collector.start();
 
   // Get camera node transform
-  LMatrix4 transform = cam_node.get_transform()->get_mat();
+  LMatrix4 transform = cam_node.get_transform().get_mat();
 
   // Get Camera and Lens pointers
   Camera *cam;

+ 4 - 4
direct/src/interval/cConstrainTransformInterval.cxx

@@ -48,10 +48,10 @@ priv_step(double t) {
   _state = S_started;
   _curr_t = t;
 
-  if(! _target.is_empty()) {
-    CPT(TransformState) transform;
-    if(_wrt) {
-      if(! _node.is_same_graph(_target)){
+  if (!_target.is_empty()) {
+    Transform transform;
+    if (_wrt) {
+      if (!_node.is_same_graph(_target)) {
         interval_cat.warning()
           << "Unable to copy transform in CConstrainTransformInterval::priv_step;\n"
           << "node (" << _node.get_name()

+ 22 - 19
direct/src/interval/cLerpNodePathInterval.cxx

@@ -109,11 +109,11 @@ priv_step(double t) {
   double d = compute_delta(t);
 
   // Save this in case we want to restore it later.
-  CPT(TransformState) prev_transform = _node.get_prev_transform();
+  Transform prev_transform = _node.get_prev_transform();
 
   if ((_flags & (F_end_pos | F_end_hpr | F_end_quat | F_end_scale | F_end_shear)) != 0) {
     // We have some transform lerp.
-    CPT(TransformState) transform;
+    Transform transform;
 
     if (_other.is_empty()) {
       // If there is no other node, it's a local transform lerp.
@@ -136,12 +136,12 @@ priv_step(double t) {
 
       } else if ((_flags & F_bake_in_start) != 0) {
         // Get the current starting pos, and bake it in.
-        set_start_pos(transform->get_pos());
+        set_start_pos(transform.get_pos());
         lerp_value(pos, d, _start_pos, _end_pos);
 
       } else {
         // "smart" lerp from the current pos to the new pos.
-        pos = transform->get_pos();
+        pos = transform.get_pos();
         lerp_value_from_prev(pos, d, _prev_d, pos, _end_pos);
       }
     }
@@ -155,11 +155,11 @@ priv_step(double t) {
         lerp_value(hpr, d, _start_hpr, _end_hpr);
 
       } else if ((_flags & F_bake_in_start) != 0) {
-        set_start_hpr(transform->get_hpr());
+        set_start_hpr(transform.get_hpr());
         lerp_value(hpr, d, _start_hpr, _end_hpr);
 
       } else {
-        hpr = transform->get_hpr();
+        hpr = transform.get_hpr();
         lerp_value_from_prev(hpr, d, _prev_d, hpr, _end_hpr);
       }
     }
@@ -174,14 +174,17 @@ priv_step(double t) {
           setup_slerp();
 
         } else if ((_flags & F_bake_in_start) != 0) {
-          set_start_quat(transform->get_norm_quat());
+          LQuaternion norm_quat = transform.get_quat();
+          norm_quat.normalize();
+          set_start_quat(norm_quat);
           setup_slerp();
 
         } else {
           if (_prev_d == 1.0) {
             _start_quat = _end_quat;
           } else {
-            LQuaternion prev_value = transform->get_norm_quat();
+            LQuaternion prev_value = transform.get_quat();
+            prev_value.normalize();
             _start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d);
           }
           setup_slerp();
@@ -199,11 +202,11 @@ priv_step(double t) {
         lerp_value(scale, d, _start_scale, _end_scale);
 
       } else if ((_flags & F_bake_in_start) != 0) {
-        set_start_scale(transform->get_scale());
+        set_start_scale(transform.get_scale());
         lerp_value(scale, d, _start_scale, _end_scale);
 
       } else {
-        scale = transform->get_scale();
+        scale = transform.get_scale();
         lerp_value_from_prev(scale, d, _prev_d, scale, _end_scale);
       }
     }
@@ -212,11 +215,11 @@ priv_step(double t) {
         lerp_value(shear, d, _start_shear, _end_shear);
 
       } else if ((_flags & F_bake_in_start) != 0) {
-        set_start_shear(transform->get_shear());
+        set_start_shear(transform.get_shear());
         lerp_value(shear, d, _start_shear, _end_shear);
 
       } else {
-        shear = transform->get_shear();
+        shear = transform.get_shear();
         lerp_value_from_prev(shear, d, _prev_d, shear, _end_shear);
       }
     }
@@ -295,19 +298,19 @@ priv_step(double t) {
       break;
 
     case F_end_pos | F_end_scale:
-      if (transform->quat_given()) {
+      /*if (transform->quat_given()) {
         if (_other.is_empty()) {
-          _node.set_pos_quat_scale(pos, transform->get_quat(), scale);
+          _node.set_pos_quat_scale(pos, transform.get_quat(), scale);
         } else {
-          _node.set_pos_quat_scale(_other, pos, transform->get_quat(), scale);
+          _node.set_pos_quat_scale(_other, pos, transform.get_quat(), scale);
         }
-      } else {
+      } else {*/
         if (_other.is_empty()) {
-          _node.set_pos_hpr_scale(pos, transform->get_hpr(), scale);
+          _node.set_pos_hpr_scale(pos, transform.get_hpr(), scale);
         } else {
-          _node.set_pos_hpr_scale(_other, pos, transform->get_hpr(), scale);
+          _node.set_pos_hpr_scale(_other, pos, transform.get_hpr(), scale);
         }
-      }
+      //}
       break;
 
     case F_end_pos | F_end_hpr | F_end_scale:

+ 17 - 11
panda/src/bullet/bulletBodyNode.cxx

@@ -353,16 +353,21 @@ do_add_shape(BulletShape *bullet_shape, const TransformState *ts) {
 
   // Reset the shape scaling before we add a shape, and remember the current
   // Scale so we can restore it later...
-  CPT(TransformState) prev_transform = get_transform();
+  Transform transform;
+  LVecBase3 prev_scale;
   bool scale_changed = false;
-  if (!prev_transform->is_identity() && prev_transform->get_scale() != LVecBase3(1.0, 1.0, 1.0)) {
-    // As a hack, temporarily release the lock, since transform_changed will
-    // otherwise deadlock trying to grab it again.  See GitHub issue #689.
-    LightMutex &lock = BulletWorld::get_global_lock();
-    lock.release();
-    set_transform(prev_transform->set_scale(LVecBase3(1.0, 1.0, 1.0)));
-    lock.acquire();
-    scale_changed = true;
+  if (get_transform(transform)) {
+    prev_scale = transform.get_scale();
+    if (prev_scale != LVecBase3(1.0, 1.0, 1.0)) {
+      // As a hack, temporarily release the lock, since transform_changed will
+      // otherwise deadlock trying to grab it again.  See GitHub issue #689.
+      LightMutex &lock = BulletWorld::get_global_lock();
+      lock.release();
+      transform.set_scale(LVecBase3(1, 1, 1));
+      set_transform(transform);
+      lock.acquire();
+      scale_changed = true;
+    }
   }
 
   // Root shape
@@ -426,10 +431,11 @@ do_add_shape(BulletShape *bullet_shape, const TransformState *ts) {
 
   // Restore the local scaling again
   if (scale_changed) {
-    CPT(TransformState) transform = get_transform()->set_scale(prev_transform->get_scale());
+    Transform transform = get_transform();
+    transform.set_scale(prev_scale);
     LightMutex &lock = BulletWorld::get_global_lock();
     lock.release();
-    set_transform(std::move(transform));
+    set_transform(transform);
     lock.acquire();
   }
 

+ 14 - 19
panda/src/bullet/bulletCharacterControllerNode.cxx

@@ -30,7 +30,7 @@ BulletCharacterControllerNode::
 BulletCharacterControllerNode(BulletShape *shape, PN_stdfloat step_height, const char *name) : BulletBaseCharacterControllerNode(name) {
 
   // Synchronised transform
-  _sync = TransformState::make_identity();
+  _sync = Transform::make_identity();
   _sync_disable = false;
 
   // Initial transform
@@ -143,18 +143,15 @@ void BulletCharacterControllerNode::
 do_sync_b2p() {
 
   NodePath np = NodePath::any_path((PandaNode *)this);
-  LVecBase3 scale = np.get_net_transform()->get_scale();
+  LVecBase3 scale = np.get_net_transform().get_scale();
 
   btTransform trans = _ghost->getWorldTransform();
-  CPT(TransformState) ts = btTrans_to_TransformState(trans, scale);
+  Transform transform = btTrans_to_Transform(trans, scale);
 
-  LMatrix4 m_sync = _sync->get_mat();
-  LMatrix4 m_ts = ts->get_mat();
-
-  if (!m_sync.almost_equal(m_ts)) {
-    _sync = ts;
+  if (!_sync.almost_equal(transform)) {
+    _sync = transform;
     _sync_disable = true;
-    np.set_transform(NodePath(), ts);
+    np.set_transform(NodePath(), transform);
     _sync_disable = false;
   }
 }
@@ -168,18 +165,16 @@ do_transform_changed() {
   if (_sync_disable) return;
 
   NodePath np = NodePath::any_path((PandaNode *)this);
-  CPT(TransformState) ts = np.get_net_transform();
-
-  LMatrix4 m_sync = _sync->get_mat();
-  LMatrix4 m_ts = ts->get_mat();
+  Transform transform = np.get_net_transform();
 
-  if (!m_sync.almost_equal(m_ts)) {
-    _sync = ts;
+  if (!_sync.almost_equal(transform)) {
+    _sync = transform;
 
     // Get translation, heading and scale
-    LPoint3 pos = ts->get_pos();
-    PN_stdfloat heading = ts->get_hpr().get_x();
-    LVecBase3 scale = ts->get_scale();
+    LPoint3 pos;
+    LVecBase3 hpr;
+    LVecBase3 scale;
+    transform.get_pos_hpr_scale(pos, hpr, scale);
 
     // Set translation
     _character->warp(LVecBase3_to_btVector3(pos));
@@ -188,7 +183,7 @@ do_transform_changed() {
     btMatrix3x3 m = _ghost->getWorldTransform().getBasis();
     btVector3 up = m[_up];
 
-    m = btMatrix3x3(btQuaternion(up, deg_2_rad(heading)));
+    m = btMatrix3x3(btQuaternion(up, deg_2_rad(hpr[0])));
 
     _ghost->getWorldTransform().setBasis(m);
 

+ 1 - 1
panda/src/bullet/bulletCharacterControllerNode.h

@@ -71,7 +71,7 @@ protected:
   virtual void transform_changed();
 
 private:
-  CPT(TransformState) _sync;
+  Transform _sync;
   bool _sync_disable;
 
   BulletUpAxis _up;

+ 6 - 9
panda/src/bullet/bulletSoftBodyNode.cxx

@@ -33,7 +33,7 @@ BulletSoftBodyNode::
 BulletSoftBodyNode(btSoftBody *body, const char *name) : BulletBodyNode(name) {
 
   // Synchronised transform
-  _sync = TransformState::make_identity();
+  _sync = Transform::make_identity();
   _sync_disable = false;
 
   // Softbody
@@ -175,15 +175,12 @@ transform_changed() {
   LightMutexHolder holder(BulletWorld::get_global_lock());
 
   NodePath np = NodePath::any_path((PandaNode *)this);
-  CPT(TransformState) ts = np.get_net_transform();
+  Transform transform = np.get_net_transform();
 
-  LMatrix4 m_sync = _sync->get_mat();
-  LMatrix4 m_ts = ts->get_mat();
-
-  if (!m_sync.almost_equal(m_ts)) {
+  if (!_sync.almost_equal(transform)) {
 
     // New transform for the center
-    btTransform trans = TransformState_to_btTrans(ts);
+    btTransform trans = Transform_to_btTrans(ts);
 
     // Offset between current approx center and current initial transform
     btVector3 pos = LVecBase3_to_btVector3(this->do_get_aabb().get_approx_center());
@@ -206,8 +203,8 @@ transform_changed() {
     _soft->transform(trans);
 
     if (ts->has_scale()) {
-      btVector3 current_scale = LVecBase3_to_btVector3(_sync->get_scale());
-      btVector3 new_scale = LVecBase3_to_btVector3(ts->get_scale());
+      btVector3 current_scale = LVecBase3_to_btVector3(_sync.get_scale());
+      btVector3 new_scale = LVecBase3_to_btVector3(transform.get_scale());
 
       current_scale.setX(1.0 / current_scale.getX());
       current_scale.setY(1.0 / current_scale.getY());

+ 1 - 1
panda/src/bullet/bulletSoftBodyNode.h

@@ -231,7 +231,7 @@ protected:
 private:
   btSoftBody *_soft;
 
-  CPT(TransformState) _sync;
+  Transform _sync;
   bool _sync_disable;
 
   PT(Geom) _geom;

+ 3 - 3
panda/src/bullet/bulletVehicle.cxx

@@ -246,14 +246,14 @@ do_sync_b2p() {
     PandaNode *node = (PandaNode *)info.m_clientInfo;
     if (node) {
 
-      CPT(TransformState) ts = btTrans_to_TransformState(info.m_worldTransform);
+      Transform transform = btTrans_to_Transform(info.m_worldTransform);
 
       // <A> Transform relative to wheel node's parent
-      // node->set_transform(ts);
+      // node->set_transform(transform);
 
       // <B> Transform absolute
       NodePath np = NodePath::any_path(node);
-      np.set_transform(np.get_top(), ts);
+      np.set_transform(np.get_top(), transform);
     }
   }
 }

+ 37 - 14
panda/src/bullet/bullet_utils.cxx

@@ -119,9 +119,20 @@ btTransform LMatrix4_to_btTrans(const LMatrix4 &m) {
  */
 LMatrix4 btTrans_to_LMatrix4(const btTransform &trans) {
 
-  return TransformState::make_pos_quat(
+  return Transform::make_pos_quat(
     btVector3_to_LVector3(trans.getOrigin()),
-    btQuat_to_LQuaternion(trans.getRotation()))->get_mat();
+    btQuat_to_LQuaternion(trans.getRotation())).get_mat();
+}
+
+/**
+ *
+ */
+Transform btTrans_to_Transform(const btTransform &trans) {
+
+  LVecBase3 pos = btVector3_to_LVector3(trans.getOrigin());
+  LQuaternion quat = btQuat_to_LQuaternion(trans.getRotation());
+
+  return Transform::make_pos_quat(pos, quat);
 }
 
 /**
@@ -135,6 +146,21 @@ CPT(TransformState) btTrans_to_TransformState(const btTransform &trans, const LV
   return TransformState::make_pos_quat_scale(pos, quat, scale);
 }
 
+/**
+ *
+ */
+btTransform Transform_to_btTrans(const Transform &transform) {
+
+  LPoint3 pos;
+  LQuaternion quat;
+  transform.get_pos_quat(pos, quat);
+
+  btQuaternion btq = LQuaternion_to_btQuat(quat);
+  btVector3 btv = LVecBase3_to_btVector3(pos);
+
+  return btTransform(btq, btv);
+}
+
 /**
  *
  */
@@ -179,26 +205,23 @@ BulletUpAxis get_default_up_axis() {
 void get_node_transform(btTransform &trans, PandaNode *node) {
 
   // Get TS
-  CPT(TransformState) ts;
+  Transform transform;
   if (node->get_num_parents() == 0) {
-    ts = node->get_transform();
+    if (!node->get_transform(transform)) {
+      trans.setIdentity();
+      return;
+    }
   }
   else {
     NodePath np = NodePath::any_path(node);
-    ts = np.get_net_transform();
+    transform = np.get_net_transform();
   }
 
   // Remove scale from TS, since scale fudges the orientation
-  ts = ts->set_scale(1.0);
+  transform.set_scale(1.0);
 
-  // Convert
-  LMatrix4 m = ts->get_mat();
-
-  LQuaternion quat;
-  quat.set_from_matrix(m.get_upper_3());
-
-  btQuaternion btq = LQuaternion_to_btQuat(quat);
-  btVector3 btv = LVecBase3_to_btVector3(m.get_row3(3));
+  btQuaternion btq = LQuaternion_to_btQuat(transform.get_quat());
+  btVector3 btv = LVecBase3_to_btVector3(transform.get_pos());
 
   trans.setRotation(btq);
   trans.setOrigin(btv);

+ 2 - 0
panda/src/bullet/bullet_utils.h

@@ -37,10 +37,12 @@ EXPCL_PANDABULLET LMatrix3 btMatrix3x3_to_LMatrix3(const btMatrix3x3 &m);
 EXPCL_PANDABULLET LMatrix4 btTrans_to_LMatrix4(const btTransform &tf);
 EXPCL_PANDABULLET LQuaternion btQuat_to_LQuaternion(const btQuaternion &q);
 
+EXPCL_PANDABULLET Transform btTrans_to_Transform(const btTransform &tf);
 EXPCL_PANDABULLET CPT(TransformState) btTrans_to_TransformState(
   const btTransform &tf,
   const LVecBase3 &scale=LVecBase3(1.0f, 1.0f, 1.0f));
 
+EXPCL_PANDABULLET btTransform Transform_to_btTrans(const Transform &transform);
 EXPCL_PANDABULLET btTransform TransformState_to_btTrans(
   CPT(TransformState) ts);
 

+ 1 - 1
panda/src/chan/animChannelMatrixDynamic.I

@@ -15,7 +15,7 @@
  * Returns the explicit TransformState value that was set via set_value(), if
  * any.
  */
-INLINE const TransformState *AnimChannelMatrixDynamic::
+INLINE const Transform &AnimChannelMatrixDynamic::
 get_value_transform() const {
   return _value;
 }

+ 22 - 20
panda/src/chan/animChannelMatrixDynamic.cxx

@@ -41,7 +41,7 @@ AnimChannelMatrixDynamic(AnimGroup *parent, const AnimChannelMatrixDynamic &copy
   AnimChannelMatrix(parent, copy),
   _value_node(copy._value_node),
   _value(copy._value),
-  _last_value(nullptr)
+  _last_value(Transform::make_identity())
 {
 }
 
@@ -52,8 +52,8 @@ AnimChannelMatrixDynamic::
 AnimChannelMatrixDynamic(const std::string &name)
   : AnimChannelMatrix(name)
 {
-  _value = TransformState::make_identity();
-  _last_value = nullptr;  // This is impossible; thus, has_changed() will
+  _value = Transform::make_identity();
+  _last_value = Transform::make_identity();  // This is impossible; thus, has_changed() will
                        // always return true the first time.
 }
 
@@ -80,7 +80,7 @@ get_value(int, LMatrix4 &mat) {
   if (_value_node != nullptr) {
     _value = _value_node->get_transform();
   }
-  mat = _value->get_mat();
+  mat = _value.get_mat();
 }
 
 /**
@@ -92,12 +92,12 @@ get_value_no_scale_shear(int, LMatrix4 &mat) {
   if (_value_node != nullptr) {
     _value = _value_node->get_transform();
   }
-  if (_value->has_scale() || _value->has_shear()) {
+  //if (_value->has_scale() || _value->has_shear()) {
     compose_matrix(mat, LVecBase3(1.0f, 1.0f, 1.0f),
-                   _value->get_hpr(), _value->get_pos());
-  } else {
-    mat = _value->get_mat();
-  }
+                   _value.get_hpr(), _value.get_pos());
+  //} else {
+  //  mat = _value.get_mat();
+  //}
 }
 
 /**
@@ -108,7 +108,7 @@ get_scale(int, LVecBase3 &scale) {
   if (_value_node != nullptr) {
     _value = _value_node->get_transform();
   }
-  scale = _value->get_scale();
+  scale = _value.get_scale();
 }
 
 /**
@@ -120,7 +120,7 @@ get_hpr(int, LVecBase3 &hpr) {
   if (_value_node != nullptr) {
     _value = _value_node->get_transform();
   }
-  hpr = _value->get_hpr();
+  hpr = _value.get_hpr();
 }
 
 /**
@@ -133,7 +133,7 @@ get_quat(int, LQuaternion &quat) {
   if (_value_node != nullptr) {
     _value = _value_node->get_transform();
   }
-  quat = _value->get_quat();
+  quat = _value.get_quat();
 }
 
 /**
@@ -145,7 +145,7 @@ get_pos(int, LVecBase3 &pos) {
   if (_value_node != nullptr) {
     _value = _value_node->get_transform();
   }
-  pos = _value->get_pos();
+  pos = _value.get_pos();
 }
 
 /**
@@ -157,7 +157,7 @@ get_shear(int, LVecBase3 &shear) {
   if (_value_node != nullptr) {
     _value = _value_node->get_transform();
   }
-  shear = _value->get_shear();
+  shear = _value.get_shear();
 }
 
 /**
@@ -165,16 +165,16 @@ get_shear(int, LVecBase3 &shear) {
  */
 void AnimChannelMatrixDynamic::
 set_value(const LMatrix4 &value) {
-  _value = TransformState::make_mat(value);
+  _value = Transform::make_mat(value);
   _value_node.clear();
 }
 
 /**
- * Explicitly sets the matrix value, using the indicated TransformState object
- * as a convenience.
+ * Explicitly sets the matrix value, using the indicated Transform object as a
+ * convenience.
  */
 void AnimChannelMatrixDynamic::
-set_value(const TransformState *value) {
+set_value(const Transform &value) {
   _value = value;
   _value_node.clear();
 }
@@ -210,7 +210,7 @@ void AnimChannelMatrixDynamic::
 write_datagram(BamWriter *manager, Datagram &dg) {
   AnimChannelMatrix::write_datagram(manager, dg);
   manager->write_pointer(dg, _value_node);
-  manager->write_pointer(dg, _value);
+  manager->write_pointer(dg, TransformState::make_mat(_value.get_mat()));
 }
 
 /**
@@ -223,7 +223,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 
   // Get the _value_node and _value pointers.
   _value_node = DCAST(PandaNode, p_list[pi++]);
-  _value = DCAST(TransformState, p_list[pi++]);
+
+  CPT(TransformState) value = DCAST(TransformState, p_list[pi++]);
+  _value = Transform::make_mat(value->get_mat());
 
   return pi;
 }

+ 4 - 4
panda/src/chan/animChannelMatrixDynamic.h

@@ -51,10 +51,10 @@ public:
 
 PUBLISHED:
   void set_value(const LMatrix4 &value);
-  void set_value(const TransformState *value);
+  void set_value(const Transform &value);
   void set_value_node(PandaNode *node);
 
-  INLINE const TransformState *get_value_transform() const;
+  INLINE const Transform &get_value_transform() const;
   INLINE PandaNode *get_value_node() const;
 
   MAKE_PROPERTY(value_node, get_value_node, set_value_node);
@@ -69,8 +69,8 @@ private:
   // frame.
   PT(PandaNode) _value_node;
 
-  CPT(TransformState) _value;
-  CPT(TransformState) _last_value;
+  Transform _value;
+  Transform _last_value;
 
 public:
   static void register_with_read_factory();

+ 1 - 1
panda/src/chan/animChannelScalarDynamic.I

@@ -19,7 +19,7 @@
 INLINE PN_stdfloat AnimChannelScalarDynamic::
 get_value() const {
   if (_value_node != nullptr) {
-    return _value->get_pos()[0];
+    return _value.get_pos()[0];
   } else {
     return _float_value;
   }

+ 7 - 5
panda/src/chan/animChannelScalarDynamic.cxx

@@ -41,7 +41,7 @@ AnimChannelScalarDynamic(AnimGroup *parent, const AnimChannelScalarDynamic &copy
   AnimChannelScalar(parent, copy),
   _value_node(copy._value_node),
   _value(copy._value),
-  _last_value(nullptr),
+  _last_value(Transform::make_identity()),
   _value_changed(true),
   _float_value(copy._float_value)
 {
@@ -54,7 +54,7 @@ AnimChannelScalarDynamic::
 AnimChannelScalarDynamic(const std::string &name)
   : AnimChannelScalar(name)
 {
-  _last_value = _value = TransformState::make_identity();
+  _last_value = _value = Transform::make_identity();
   _value_changed = true;
   _float_value = 0.0;
 }
@@ -106,7 +106,7 @@ set_value(PN_stdfloat value) {
 void AnimChannelScalarDynamic::
 set_value_node(PandaNode *value_node) {
   if (_value_node == nullptr) {
-    _last_value = TransformState::make_pos(LVecBase3(_float_value, 0.0f, 0.0f));
+    _last_value = Transform::make_pos(LVecBase3(_float_value, 0.0f, 0.0f));
   }
 
   _value_node = value_node;
@@ -135,7 +135,7 @@ void AnimChannelScalarDynamic::
 write_datagram(BamWriter *manager, Datagram &dg) {
   AnimChannelScalar::write_datagram(manager, dg);
   manager->write_pointer(dg, _value_node);
-  manager->write_pointer(dg, _value);
+  manager->write_pointer(dg, TransformState::make_mat(_value.get_mat()));
   dg.add_stdfloat(_float_value);
 }
 
@@ -149,7 +149,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
 
   // Get the _value_node and _value pointers.
   _value_node = DCAST(PandaNode, p_list[pi++]);
-  _value = DCAST(TransformState, p_list[pi++]);
+
+  CPT(TransformState) value = DCAST(TransformState, p_list[pi++]);
+  _value = Transform::make_mat(value->get_mat());
 
   return pi;
 }

+ 2 - 2
panda/src/chan/animChannelScalarDynamic.h

@@ -59,8 +59,8 @@ private:
   // get an implicit value from the transform on the indicated node each
   // frame.
   PT(PandaNode) _value_node;
-  CPT(TransformState) _value;
-  CPT(TransformState) _last_value;
+  Transform _value;
+  Transform _last_value;
 
   // This is used only if we are using the explicit set_value() interface.
   bool _value_changed;

+ 5 - 1
panda/src/chan/partBundleNode.cxx

@@ -44,10 +44,14 @@ void PartBundleNode::
 apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
                           GeomTransformer &transformer) {
   if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    if (attribs._transform_state == nullptr) {
+      attribs._transform_state = TransformState::make_mat(attribs._transform.get_mat());
+    }
+
     LightMutexHolder holder(_lock);
     for (PT(PartBundleHandle) handle : _bundles) {
       PartBundle *bundle = handle->get_bundle();
-      PT(PartBundle) new_bundle = bundle->apply_transform(attribs._transform);
+      PT(PartBundle) new_bundle = bundle->apply_transform(attribs._transform_state);
       update_bundle(handle, new_bundle);
     }
 

+ 8 - 8
panda/src/char/character.cxx

@@ -171,8 +171,8 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
   if (_do_lod_animation) {
     int this_frame = ClockObject::get_global_clock()->get_frame_count();
 
-    CPT(TransformState) rel_transform = get_rel_transform(trav, data);
-    LPoint3 center = _lod_center * rel_transform->get_mat();
+    Transform rel_transform = get_rel_transform(trav, data);
+    LPoint3 center = rel_transform.xform_point(_lod_center);
     PN_stdfloat dist2 = center.dot(center);
 
     if (this_frame != _view_frame || dist2 < _view_distance2) {
@@ -215,9 +215,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
  * This function is recursive, and the return value is the transform after it
  * has been modified by this node's transform.
  */
-CPT(TransformState) Character::
+Transform Character::
 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
-                  const TransformState *transform, Thread *current_thread) const {
+                  const Transform &transform, Thread *current_thread) const {
   // This method is overridden by Character solely to provide a hook to force
   // the joints to update before computing the bounding volume.
   ((Character *)this)->update_to_now();
@@ -510,23 +510,23 @@ update_bundle(PartBundleHandle *old_bundle_handle, PartBundle *new_bundle) {
  * Returns the relative transform to convert from the LODNode space to the
  * camera space.
  */
-CPT(TransformState) Character::
+Transform Character::
 get_rel_transform(CullTraverser *trav, CullTraverserData &data) {
   // Get a pointer to the camera node.
   Camera *camera = trav->get_scene()->get_camera_node();
 
   // Get the camera space transform.
-  CPT(TransformState) rel_transform;
+  Transform rel_transform;
 
   NodePath lod_center = camera->get_lod_center();
   if (!lod_center.is_empty()) {
     rel_transform =
-      lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
+      lod_center.get_net_transform().invert_compose(data.get_net_transform(trav));
   } else {
     NodePath cull_center = camera->get_cull_center();
     if (!cull_center.is_empty()) {
       rel_transform =
-        cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
+        cull_center.get_net_transform().invert_compose(data.get_net_transform(trav));
     } else {
       rel_transform = data.get_modelview_transform(trav);
     }

+ 3 - 4
panda/src/char/character.h

@@ -49,10 +49,9 @@ public:
 
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
 
-  virtual CPT(TransformState)
+  virtual Transform
     calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
-                      bool &found_any,
-                      const TransformState *transform,
+                      bool &found_any, const Transform &transform,
                       Thread *current_thread) const;
 
 PUBLISHED:
@@ -82,7 +81,7 @@ protected:
                                Thread *current_thread);
   virtual void update_bundle(PartBundleHandle *old_bundle_handle,
                              PartBundle *new_bundle);
-  CPT(TransformState) get_rel_transform(CullTraverser *trav, CullTraverserData &data);
+  Transform get_rel_transform(CullTraverser *trav, CullTraverserData &data);
 
 private:
   void do_update();

+ 5 - 5
panda/src/char/characterJoint.cxx

@@ -134,7 +134,7 @@ update_internals(PartBundle *root, PartGroup *parent, bool self_changed,
 
   if (net_changed) {
     if (!_net_transform_nodes.empty()) {
-      CPT(TransformState) t = TransformState::make_mat(_net_transform);
+      Transform t = Transform::make_mat(_net_transform);
 
       NodeList::iterator ai;
       for (ai = _net_transform_nodes.begin();
@@ -157,7 +157,7 @@ update_internals(PartBundle *root, PartGroup *parent, bool self_changed,
   }
 
   if (self_changed && !_local_transform_nodes.empty()) {
-    CPT(TransformState) t = TransformState::make_mat(_value);
+    Transform t = Transform::make_mat(_value);
 
     NodeList::iterator ai;
     for (ai = _local_transform_nodes.begin();
@@ -197,7 +197,7 @@ add_net_transform(PandaNode *node) {
   if (_character != nullptr) {
     node->set_effect(CharacterJointEffect::make(_character));
   }
-  CPT(TransformState) t = TransformState::make_mat(_net_transform);
+  Transform t = Transform::make_mat(_net_transform);
   node->set_transform(t, Thread::get_current_thread());
   return _net_transform_nodes.insert(node).second;
 }
@@ -289,7 +289,7 @@ add_local_transform(PandaNode *node) {
   if (_character != nullptr) {
     node->set_effect(CharacterJointEffect::make(_character));
   }
-  CPT(TransformState) t = TransformState::make_mat(_value);
+  Transform t = Transform::make_mat(_value);
   node->set_transform(t, Thread::get_current_thread());
   return _local_transform_nodes.insert(node).second;
 }
@@ -374,7 +374,7 @@ get_transform(LMatrix4 &transform) const {
 
 CPT(TransformState) CharacterJoint::
 get_transform_state() const {
-    return TransformState::make_mat( _value );
+  return TransformState::make_mat(_value);
 }
 
 /**

+ 3 - 3
panda/src/char/characterJointEffect.cxx

@@ -121,7 +121,7 @@ has_cull_callback() const {
  */
 void CharacterJointEffect::
 cull_callback(CullTraverser *trav, CullTraverserData &data,
-              CPT(TransformState) &node_transform,
+              Transform &node_transform,
               CPT(RenderState) &) const {
   if (auto character = _character.lock()) {
     character->update();
@@ -148,8 +148,8 @@ has_adjust_transform() const {
  * they may (or may not) be modified in-place by the RenderEffect.
  */
 void CharacterJointEffect::
-adjust_transform(CPT(TransformState) &net_transform,
-                 CPT(TransformState) &node_transform,
+adjust_transform(Transform &net_transform,
+                 Transform &node_transform,
                  const PandaNode *node) const {
   if (auto character = _character.lock()) {
     character->update();

+ 3 - 3
panda/src/char/characterJointEffect.h

@@ -49,12 +49,12 @@ public:
 
   virtual bool has_cull_callback() const;
   virtual void cull_callback(CullTraverser *trav, CullTraverserData &data,
-                             CPT(TransformState) &node_transform,
+                             Transform &node_transform,
                              CPT(RenderState) &node_state) const;
 
   virtual bool has_adjust_transform() const;
-  virtual void adjust_transform(CPT(TransformState) &net_transform,
-                                CPT(TransformState) &node_transform,
+  virtual void adjust_transform(Transform &net_transform,
+                                Transform &node_transform,
                                 const PandaNode *node) const;
 
 protected:

+ 11 - 12
panda/src/collide/collisionBox.cxx

@@ -185,12 +185,10 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), nullptr);
 
-  CPT(TransformState) wrt_space = entry.get_wrt_space();
-  CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
+  Transform wrt_space = entry.get_wrt_space();
+  Transform wrt_prev_space = entry.get_wrt_prev_space();
 
-  const LMatrix4 &wrt_mat = wrt_space->get_mat();
-
-  LPoint3 orig_center = sphere->get_center() * wrt_mat;
+  LPoint3 orig_center = wrt_space.xform_point(sphere->get_center());
   LPoint3 from_center = orig_center;
   bool moved_from_center = false;
   PN_stdfloat t = 1.0f;
@@ -198,7 +196,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   PN_stdfloat actual_t = 1.0f;
 
   LVector3 from_radius_v =
-    LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
+    wrt_space.xform_vec(LVector3(sphere->get_radius(), 0.0f, 0.0f));
   PN_stdfloat from_radius_2 = from_radius_v.length_squared();
   PN_stdfloat from_radius = csqrt(from_radius_2);
 
@@ -213,12 +211,13 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   for(ip = 0, intersect = false; ip < 6 && !intersect; ip++) {
     plane = get_plane(ip);
 
-    if (wrt_prev_space != wrt_space) {
+    LPoint3 prev_center = wrt_prev_space.xform_point(sphere->get_center());
+    if (orig_center != prev_center) {
       // If we have a delta between the previous position and the current
       // position, we use that to determine some more properties of the
       // collision.
       LPoint3 b = from_center;
-      LPoint3 a = sphere->get_center() * wrt_prev_space->get_mat();
+      LPoint3 a = prev_center;
       LVector3 delta = b - a;
 
       // First, there is no collision if the "from" object is definitely
@@ -1097,7 +1096,7 @@ intersects_line(double &t1, double &t2,
 bool CollisionBox::
 apply_clip_plane(CollisionBox::Points &new_points,
                  const ClipPlaneAttrib *cpa,
-                 const TransformState *net_transform, int plane_no) const {
+                 const Transform &net_transform, int plane_no) const {
   bool all_in = true;
 
   int num_planes = cpa->get_num_on_planes();
@@ -1107,10 +1106,10 @@ apply_clip_plane(CollisionBox::Points &new_points,
     NodePath plane_path = cpa->get_on_plane(i);
     PlaneNode *plane_node = DCAST(PlaneNode, plane_path.node());
     if ((plane_node->get_clip_effect() & PlaneNode::CE_collision) != 0) {
-      CPT(TransformState) new_transform =
-        net_transform->invert_compose(plane_path.get_net_transform());
+      Transform new_transform =
+        net_transform.invert_compose(plane_path.get_net_transform());
 
-      LPlane plane = plane_node->get_plane() * new_transform->get_mat();
+      LPlane plane = plane_node->get_plane() * new_transform.get_mat();
       if (first_plane) {
         first_plane = false;
         if (!clip_polygon(new_points, _points[plane_no], 4, plane, plane_no)) {

+ 1 - 1
panda/src/collide/collisionBox.h

@@ -144,7 +144,7 @@ public:
                     size_t num_source_points, const LPlane &plane,
                     int plane_no) const;
   bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa,
-                        const TransformState *net_transform, int plane_no) const;
+                        const Transform &net_transform, int plane_no) const;
 
 private:
   PointDef _points[6][4]; // one set of points for each of the six planes that make up the box

+ 7 - 11
panda/src/collide/collisionCapsule.cxx

@@ -30,7 +30,7 @@
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "cmath.h"
-#include "transformState.h"
+#include "transform.h"
 #include "geom.h"
 #include "geomTristrips.h"
 #include "geomVertexWriter.h"
@@ -230,26 +230,22 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), nullptr);
 
-  CPT(TransformState) wrt_space = entry.get_wrt_space();
-  CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
+  Transform wrt_space = entry.get_wrt_space();
+  Transform wrt_prev_space = entry.get_wrt_prev_space();
 
-  const LMatrix4 &wrt_mat = wrt_space->get_mat();
-
-  LPoint3 from_a = sphere->get_center() * wrt_mat;
+  LPoint3 from_a = wrt_space.xform_point(sphere->get_center());
   LPoint3 from_b = from_a;
 
   LPoint3 contact_point;
   PN_stdfloat actual_t = 0.0f;
 
-  if (wrt_prev_space != wrt_space) {
-    // If the sphere is moving relative to the capsule, it becomes a capsule itself.
-    from_a = sphere->get_center() * wrt_prev_space->get_mat();
-  }
+  // If the sphere is moving relative to the capsule, it becomes a capsule itself.
+  from_a = wrt_prev_space.xform_point(sphere->get_center());
 
   LVector3 from_direction = from_b - from_a;
 
   LVector3 from_radius_v =
-    LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
+    wrt_space.xform_vec(LVector3(sphere->get_radius(), 0.0f, 0.0f));
   PN_stdfloat from_radius = length(from_radius_v);
 
   double t1, t2;

+ 9 - 9
panda/src/collide/collisionEntry.I

@@ -261,7 +261,7 @@ has_contact_normal() const {
 /**
  * Returns the relative transform of the from node as seen from the into node.
  */
-INLINE CPT(TransformState) CollisionEntry::
+INLINE Transform CollisionEntry::
 get_wrt_space() const {
   return _from_node_path.get_transform(_into_node_path);
 }
@@ -269,7 +269,7 @@ get_wrt_space() const {
 /**
  * Returns the relative transform of the into node as seen from the from node.
  */
-INLINE CPT(TransformState) CollisionEntry::
+INLINE Transform CollisionEntry::
 get_inv_wrt_space() const {
   return _into_node_path.get_transform(_from_node_path);
 }
@@ -279,7 +279,7 @@ get_inv_wrt_space() const {
  * as of the previous frame (according to set_prev_transform(),
  * set_fluid_pos(), etc.)
  */
-INLINE CPT(TransformState) CollisionEntry::
+INLINE Transform CollisionEntry::
 get_wrt_prev_space() const {
   if (get_respect_prev_transform()) {
     return _from_node_path.get_prev_transform(_into_node_path);
@@ -291,17 +291,17 @@ get_wrt_prev_space() const {
 /**
  * Returns the relative transform of the from node as seen from the into node.
  */
-INLINE const LMatrix4 &CollisionEntry::
+INLINE LMatrix4 CollisionEntry::
 get_wrt_mat() const {
-  return get_wrt_space()->get_mat();
+  return get_wrt_space().get_mat();
 }
 
 /**
  * Returns the relative transform of the into node as seen from the from node.
  */
-INLINE const LMatrix4 &CollisionEntry::
+INLINE LMatrix4 CollisionEntry::
 get_inv_wrt_mat() const {
-  return get_inv_wrt_space()->get_mat();
+  return get_inv_wrt_space().get_mat();
 }
 
 /**
@@ -309,9 +309,9 @@ get_inv_wrt_mat() const {
  * as of the previous frame (according to set_prev_transform(),
  * set_fluid_pos(), etc.)
  */
-INLINE const LMatrix4 &CollisionEntry::
+INLINE LMatrix4 CollisionEntry::
 get_wrt_prev_mat() const {
-  return get_wrt_prev_space()->get_mat();
+  return get_wrt_prev_space().get_mat();
 }
 
 /**

+ 12 - 19
panda/src/collide/collisionEntry.cxx

@@ -72,8 +72,7 @@ operator = (const CollisionEntry &copy) {
 LPoint3 CollisionEntry::
 get_surface_point(const NodePath &space) const {
   nassertr(has_surface_point(), LPoint3::zero());
-  CPT(TransformState) transform = _into_node_path.get_transform(space);
-  return _surface_point * transform->get_mat();
+  return _into_node_path.get_transform(space).xform_point(_surface_point);
 }
 
 /**
@@ -86,8 +85,7 @@ get_surface_point(const NodePath &space) const {
 LVector3 CollisionEntry::
 get_surface_normal(const NodePath &space) const {
   nassertr(has_surface_normal(), LVector3::zero());
-  CPT(TransformState) transform = _into_node_path.get_transform(space);
-  return _surface_normal * transform->get_mat();
+  return _into_node_path.get_transform(space).xform_vec(_surface_normal);
 }
 
 /**
@@ -105,8 +103,7 @@ get_interior_point(const NodePath &space) const {
   if (!has_interior_point()) {
     return get_surface_point(space);
   }
-  CPT(TransformState) transform = _into_node_path.get_transform(space);
-  return _interior_point * transform->get_mat();
+  return _into_node_path.get_transform(space).xform_point(_interior_point);
 }
 
 /**
@@ -119,29 +116,28 @@ get_interior_point(const NodePath &space) const {
 bool CollisionEntry::
 get_all(const NodePath &space, LPoint3 &surface_point,
         LVector3 &surface_normal, LPoint3 &interior_point) const {
-  CPT(TransformState) transform = _into_node_path.get_transform(space);
-  const LMatrix4 &mat = transform->get_mat();
+  Transform transform = _into_node_path.get_transform(space);
   bool all_ok = true;
 
   if (!has_surface_point()) {
     surface_point = LPoint3::zero();
     all_ok = false;
   } else {
-    surface_point = _surface_point * mat;
+    surface_point = transform.xform_point(_surface_point);
   }
 
   if (!has_surface_normal()) {
     surface_normal = LVector3::zero();
     all_ok = false;
   } else {
-    surface_normal = _surface_normal * mat;
+    surface_normal = transform.xform_vec(_surface_normal);
   }
 
   if (!has_interior_point()) {
     interior_point = surface_point;
     all_ok = false;
   } else {
-    interior_point = _interior_point * mat;
+    interior_point = transform.xform_point(_interior_point);
   }
 
   return all_ok;
@@ -157,8 +153,7 @@ get_all(const NodePath &space, LPoint3 &surface_point,
 LPoint3 CollisionEntry::
 get_contact_pos(const NodePath &space) const {
   nassertr(has_contact_pos(), LPoint3::zero());
-  CPT(TransformState) transform = _into_node_path.get_transform(space);
-  return _contact_pos * transform->get_mat();
+  return _into_node_path.get_transform(space).xform_point(_contact_pos);
 }
 
 /**
@@ -170,8 +165,7 @@ get_contact_pos(const NodePath &space) const {
 LVector3 CollisionEntry::
 get_contact_normal(const NodePath &space) const {
   nassertr(has_contact_normal(), LVector3::zero());
-  CPT(TransformState) transform = _into_node_path.get_transform(space);
-  return _contact_normal * transform->get_mat();
+  return _into_node_path.get_transform(space).xform_vec(_contact_normal);
 }
 
 /**
@@ -184,22 +178,21 @@ get_contact_normal(const NodePath &space) const {
 bool CollisionEntry::
 get_all_contact_info(const NodePath &space, LPoint3 &contact_pos,
                      LVector3 &contact_normal) const {
-  CPT(TransformState) transform = _into_node_path.get_transform(space);
-  const LMatrix4 &mat = transform->get_mat();
+  Transform transform = _into_node_path.get_transform(space);
   bool all_ok = true;
 
   if (!has_contact_pos()) {
     contact_pos = LPoint3::zero();
     all_ok = false;
   } else {
-    contact_pos = _contact_pos * mat;
+    contact_pos = transform.xform_point(_contact_pos);
   }
 
   if (!has_contact_normal()) {
     contact_normal = LVector3::zero();
     all_ok = false;
   } else {
-    contact_normal = _contact_normal * mat;
+    contact_normal = transform.xform_vec(_contact_normal);
   }
 
   return all_ok;

+ 6 - 6
panda/src/collide/collisionEntry.h

@@ -105,13 +105,13 @@ PUBLISHED:
   MAKE_PROPERTY(respect_prev_transform, get_respect_prev_transform);
 
 public:
-  INLINE CPT(TransformState) get_wrt_space() const;
-  INLINE CPT(TransformState) get_inv_wrt_space() const;
-  INLINE CPT(TransformState) get_wrt_prev_space() const;
+  INLINE Transform get_wrt_space() const;
+  INLINE Transform get_inv_wrt_space() const;
+  INLINE Transform get_wrt_prev_space() const;
 
-  INLINE const LMatrix4 &get_wrt_mat() const;
-  INLINE const LMatrix4 &get_inv_wrt_mat() const;
-  INLINE const LMatrix4 &get_wrt_prev_mat() const;
+  INLINE LMatrix4 get_wrt_mat() const;
+  INLINE LMatrix4 get_inv_wrt_mat() const;
+  INLINE LMatrix4 get_wrt_prev_mat() const;
 
   INLINE const ClipPlaneAttrib *get_into_clip_planes() const;
 

+ 4 - 3
panda/src/collide/collisionHandlerFloor.cxx

@@ -223,10 +223,11 @@ handle_entries() {
             adjust = std::max(adjust, -max_adjust);
           }
 
-          CPT(TransformState) trans = def._target.get_transform();
-          LVecBase3 pos = trans->get_pos();
+          Transform trans = def._target.get_transform();
+          LVecBase3 pos = trans.get_pos();
           pos[2] += adjust;
-          def._target.set_transform(trans->set_pos(pos));
+          trans.set_pos(pos);
+          def._target.set_transform(trans);
           def.updated_transform();
 
           apply_linear_force(def, LVector3(0.0f, 0.0f, adjust));

+ 8 - 9
panda/src/collide/collisionHandlerFluidPusher.cxx

@@ -131,16 +131,15 @@ handle_entries() {
       LVector3 N(M);
 
       const LPoint3 orig_pos(from_node_path.get_pos(wrt_node));
-      CPT(TransformState) prev_trans(from_node_path.get_prev_transform(wrt_node));
-      const LPoint3 orig_prev_pos(prev_trans->get_pos());
+      Transform prev_trans(from_node_path.get_prev_transform(wrt_node));
+      const LPoint3 orig_prev_pos(prev_trans.get_pos());
 
       // currently we only support spheres as the collider
       const CollisionSphere *sphere;
       DCAST_INTO_R(sphere, entries.front()->get_from(), false);
 
       from_node_path.set_pos(wrt_node, 0,0,0);
-      LPoint3 sphere_offset = (sphere->get_center() *
-                                from_node_path.get_transform(wrt_node)->get_mat());
+      LPoint3 sphere_offset = from_node_path.get_transform(wrt_node).xform_point(sphere->get_center());
       from_node_path.set_pos(wrt_node, orig_pos);
 
       // this will hold the final calculated position at each iteration
@@ -209,14 +208,14 @@ handle_entries() {
         }
 
         from_node_path.set_pos(wrt_node, candidate_final_pos);
-        CPT(TransformState) prev_trans(from_node_path.get_prev_transform(wrt_node));
-        prev_trans = prev_trans->set_pos(contact_pos);
+        Transform prev_trans(from_node_path.get_prev_transform(wrt_node));
+        prev_trans.set_pos(contact_pos);
         from_node_path.set_prev_transform(wrt_node, prev_trans);
 
         /*{
           const LPoint3 new_pos(from_node_path.get_pos(wrt_node));
-          CPT(TransformState) new_prev_trans(from_node_path.get_prev_transform(wrt_node));
-          const LPoint3 new_prev_pos(new_prev_trans->get_pos());
+          Transform new_prev_trans(from_node_path.get_prev_transform(wrt_node));
+          const LPoint3 new_prev_pos(new_prev_trans.get_pos());
         }*/
 
         // recalculate the position delta
@@ -245,7 +244,7 @@ handle_entries() {
       from_node_path.set_pos(wrt_node, orig_pos);
       // restore the appropriate previous position
       prev_trans = from_node_path.get_prev_transform(wrt_node);
-      prev_trans = prev_trans->set_pos(orig_prev_pos);
+      prev_trans.set_pos(orig_prev_pos);
       from_node_path.set_prev_transform(wrt_node, prev_trans);
 
       LVector3 net_shove(candidate_final_pos - orig_pos);

+ 6 - 5
panda/src/collide/collisionHandlerGravity.cxx

@@ -223,8 +223,8 @@ set_highest_collision(const NodePath &target_node_path, const NodePath &from_nod
  * what is the normal of the avatar that the avatar is colliding with relative
  * to the plane.
  */
-    CPT(TransformState) transform = highest->get_into_node_path().get_transform(from_node_path);
-    _contact_normal = DCAST(CollisionPlane, highest->get_into())->get_normal() * transform->get_mat();
+    Transform transform = highest->get_into_node_path().get_transform(from_node_path);
+    _contact_normal = transform.xform_vec(DCAST(CollisionPlane, highest->get_into())->get_normal());
   } else {
     _contact_normal = highest->get_surface_normal(from_node_path);
   }
@@ -307,10 +307,11 @@ handle_entries() {
           _current_colliding.clear();
         }
 
-        CPT(TransformState) trans = def._target.get_transform();
-        LVecBase3 pos = trans->get_pos();
+        Transform trans = def._target.get_transform();
+        LVecBase3 pos = trans.get_pos();
         pos[2] += adjust;
-        def._target.set_transform(trans->set_pos(pos));
+        trans.set_pos(pos);
+        def._target.set_transform(trans);
         def.updated_transform();
 
         apply_linear_force(def, LVector3(0.0f, 0.0f, adjust));

+ 5 - 4
panda/src/collide/collisionHandlerPusher.cxx

@@ -249,10 +249,11 @@ handle_entries() {
           #endif
 
           // This is the part where the node actually gets moved:
-          CPT(TransformState) trans = def._target.get_transform();
-          LVecBase3 pos = trans->get_pos();
-          pos += net_shove * trans->get_mat();
-          def._target.set_transform(trans->set_pos(pos));
+          Transform trans = def._target.get_transform();
+          LVecBase3 pos = trans.get_pos();
+          pos += trans.xform_vec(net_shove);
+          trans.set_pos(pos);
+          def._target.set_transform(trans);
           def.updated_transform();
 
           // We call this to allow derived classes to do other fix-ups as they

+ 3 - 4
panda/src/collide/collisionHeightfield.cxx

@@ -279,11 +279,10 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), nullptr);
 
-  CPT(TransformState) wrt_space = entry.get_wrt_space();
-  const LMatrix4 &wrt_mat = wrt_space->get_mat();
+  Transform wrt_space = entry.get_wrt_space();
 
-  LPoint3 center = sphere->get_center() * wrt_mat;
-  LVector3 radius_v = LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
+  LPoint3 center = wrt_space.xform_point(sphere->get_center());
+  LVector3 radius_v = wrt_space.xform_vec(LVector3(sphere->get_radius(), 0.0f, 0.0f));
   PN_stdfloat radius_2 = radius_v.length_squared();
   PN_stdfloat radius = csqrt(radius_2);
 

+ 4 - 10
panda/src/collide/collisionLevelState.I

@@ -344,16 +344,10 @@ apply_transform() {
   } else {
     // Otherwise, in the usual case, the bounds tests will continue.
     // Recompute the bounds list of this node (if we have a transform).
-    const TransformState *node_transform = node()->get_transform();
-    if (!node_transform->is_identity()) {
-      CPT(TransformState) inv_transform =
-        node_transform->invert_compose(TransformState::make_identity());
-      if (!inv_transform->has_mat()) {
-        // No inverse.
-        return false;
-      }
-
-      const LMatrix4 &mat = inv_transform->get_mat();
+    Transform node_transform;
+    if (node()->get_transform(node_transform)) {
+      Transform inv_transform = node_transform.get_inverse();
+      LMatrix4 mat = inv_transform.get_mat();
 
       // Now build the new bounding volumes list.
       BoundingVolumes new_bounds;

+ 2 - 2
panda/src/collide/collisionLevelStateBase.cxx

@@ -83,8 +83,8 @@ prepare_collider(const ColliderDef &def, const NodePath &root) {
       }
     }
 
-    CPT(TransformState) rel_transform = def._node_path.get_transform(root.get_parent());
-    gbv->xform(rel_transform->get_mat());
+    Transform rel_transform = def._node_path.get_transform(root.get_parent());
+    gbv->xform(rel_transform.get_mat());
     _local_bounds.push_back(gbv);
   }
 

+ 3 - 3
panda/src/collide/collisionNode.cxx

@@ -196,9 +196,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
     // Determine the previous frame's position, relative to the current
     // position.
     NodePath node_path = data.get_node_path();
-    CPT(TransformState) transform = node_path.get_net_transform()->invert_compose(node_path.get_net_prev_transform());
+    Transform transform = node_path.get_net_transform().invert_compose(node_path.get_net_prev_transform());
 
-    if (!transform->is_identity()) {
+    if (!transform.is_identity()) {
       // If we have a velocity, also draw the previous frame's position,
       // ghosted.
 
@@ -207,7 +207,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
         PT(PandaNode) node = solid->get_viz(trav, data, false);
         if (node != nullptr) {
           trav->traverse_down(data, node,
-            data._net_transform->compose(transform), get_last_pos_state());
+            data._net_transform.compose(transform), get_last_pos_state());
         }
       }
     }

+ 28 - 32
panda/src/collide/collisionPolygon.cxx

@@ -388,12 +388,10 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), nullptr);
 
-  CPT(TransformState) wrt_space = entry.get_wrt_space();
-  CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
+  Transform wrt_space = entry.get_wrt_space();
+  Transform wrt_prev_space = entry.get_wrt_prev_space();
 
-  const LMatrix4 &wrt_mat = wrt_space->get_mat();
-
-  LPoint3 orig_center = sphere->get_center() * wrt_mat;
+  LPoint3 orig_center = wrt_space.xform_point(sphere->get_center());
   LPoint3 from_center = orig_center;
   bool moved_from_center = false;
   PN_stdfloat t = 1.0f;
@@ -401,16 +399,17 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   PN_stdfloat actual_t = 1.0f;
 
   LVector3 from_radius_v =
-    LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
+    wrt_space.xform_vec(LVector3(sphere->get_radius(), 0.0f, 0.0f));
   PN_stdfloat from_radius_2 = from_radius_v.length_squared();
   PN_stdfloat from_radius = csqrt(from_radius_2);
 
-  if (wrt_prev_space != wrt_space) {
+  LPoint3 prev_center = wrt_prev_space.xform_point(sphere->get_center());
+  if (orig_center != prev_center) {
     // If we have a delta between the previous position and the current
     // position, we use that to determine some more properties of the
     // collision.
     LPoint3 b = from_center;
-    LPoint3 a = sphere->get_center() * wrt_prev_space->get_mat();
+    LPoint3 a = prev_center;
     LVector3 delta = b - a;
 
     // First, there is no collision if the "from" object is definitely moving
@@ -795,13 +794,12 @@ test_intersection_from_capsule(const CollisionEntry &entry) const {
   const CollisionCapsule *capsule;
   DCAST_INTO_R(capsule, entry.get_from(), nullptr);
 
-  CPT(TransformState) wrt_space = entry.get_wrt_space();
-  const LMatrix4 &wrt_mat = wrt_space->get_mat();
+  Transform wrt_space = entry.get_wrt_space();
 
-  LPoint3 from_a = capsule->get_point_a() * wrt_mat;
-  LPoint3 from_b = capsule->get_point_b() * wrt_mat;
+  LPoint3 from_a = wrt_space.xform_point(capsule->get_point_a());
+  LPoint3 from_b = wrt_space.xform_point(capsule->get_point_b());
   LVector3 from_radius_v =
-    LVector3(capsule->get_radius(), 0.0f, 0.0f) * wrt_mat;
+    wrt_space.xform_vec(LVector3(capsule->get_radius(), 0.0f, 0.0f));
   PN_stdfloat from_radius_2 = from_radius_v.length_squared();
   PN_stdfloat from_radius = csqrt(from_radius_2);
 
@@ -821,17 +819,18 @@ test_intersection_from_capsule(const CollisionEntry &entry) const {
       return nullptr;
     }
 
-    CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
-    if (wrt_prev_space == wrt_space) {
-      return nullptr;
-    }
+    Transform wrt_prev_space = entry.get_wrt_prev_space();
 
     // Note that we only check for a sphere at the center, since we don't
     // know whether the capsule has undergone any rotation.
-    LPoint3 from_center = LPoint3((from_a + from_b) * 0.5f) * _to_2d_mat;
-    LPoint3 prev_center =
-      (LPoint3((capsule->get_point_a() + capsule->get_point_b()) * 0.5f) *
-       wrt_prev_space->get_mat()) * _to_2d_mat;
+    LPoint3 from_center = (from_a + from_b) * 0.5f;
+    LPoint3 prev_center = wrt_prev_space.xform_point((capsule->get_point_a() + capsule->get_point_b()) * 0.5f);
+    if (from_center == prev_center) {
+      return nullptr;
+    }
+
+    from_center = from_center * _to_2d_mat;
+    prev_center = prev_center * _to_2d_mat;
 
     if (prev_center[1] > 0) {
       // The center didn't pass through the polygon.
@@ -1134,8 +1133,8 @@ test_intersection_from_box(const CollisionEntry &entry) const {
 
   // To make things easier, transform the box into the coordinate space of the
   // plane.
-  CPT(TransformState) wrt_space = entry.get_wrt_space();
-  const LMatrix4 &wrt_mat = wrt_space->get_mat();
+  Transform wrt_space = entry.get_wrt_space();
+  LMatrix4 wrt_mat = wrt_space.get_mat();
   LMatrix4 plane_mat = wrt_mat * _to_2d_mat;
 
   LPoint3 from_center = box->get_center() * plane_mat;
@@ -1215,13 +1214,10 @@ test_intersection_from_box(const CollisionEntry &entry) const {
       return nullptr;
     }
 
-    CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
-    if (wrt_prev_space == wrt_space) {
-      return nullptr;
-    }
+    Transform wrt_prev_space = entry.get_wrt_prev_space();
 
     // Did the center travel into the plane of the polygon?
-    LPoint3 prev_center = box->get_center() * (wrt_prev_space->get_mat() * _to_2d_mat);
+    LPoint3 prev_center = wrt_prev_space.xform_point(box->get_center()) * _to_2d_mat;
     if (prev_center[1] > 0.0f) {
       // Nope, it did not.
       return nullptr;
@@ -1781,7 +1777,7 @@ clip_polygon(CollisionPolygon::Points &new_points,
 bool CollisionPolygon::
 apply_clip_plane(CollisionPolygon::Points &new_points,
                  const ClipPlaneAttrib *cpa,
-                 const TransformState *net_transform) const {
+                 const Transform &net_transform) const {
   bool all_in = true;
 
   int num_planes = cpa->get_num_on_planes();
@@ -1791,10 +1787,10 @@ apply_clip_plane(CollisionPolygon::Points &new_points,
     NodePath plane_path = cpa->get_on_plane(i);
     PlaneNode *plane_node = DCAST(PlaneNode, plane_path.node());
     if ((plane_node->get_clip_effect() & PlaneNode::CE_collision) != 0) {
-      CPT(TransformState) new_transform =
-        net_transform->invert_compose(plane_path.get_net_transform());
+      Transform new_transform =
+        net_transform.invert_compose(plane_path.get_net_transform());
 
-      LPlane plane = plane_node->get_plane() * new_transform->get_mat();
+      LPlane plane = plane_node->get_plane() * new_transform.get_mat();
       if (first_plane) {
         first_plane = false;
         if (!clip_polygon(new_points, _points, plane)) {

+ 1 - 1
panda/src/collide/collisionPolygon.h

@@ -141,7 +141,7 @@ private:
   bool clip_polygon(Points &new_points, const Points &source_points,
                     const LPlane &plane) const;
   bool apply_clip_plane(Points &new_points, const ClipPlaneAttrib *cpa,
-                        const TransformState *net_transform) const;
+                        const Transform &net_transform) const;
 
 private:
   Points _points;

+ 7 - 10
panda/src/collide/collisionSphere.cxx

@@ -127,17 +127,14 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   const CollisionSphere *sphere;
   DCAST_INTO_R(sphere, entry.get_from(), nullptr);
 
-  CPT(TransformState) wrt_space = entry.get_wrt_space();
+  Transform wrt_space = entry.get_wrt_space();
 
-  const LMatrix4 &wrt_mat = wrt_space->get_mat();
-
-  LPoint3 from_b = sphere->get_center() * wrt_mat;
+  LPoint3 from_b = wrt_space.xform_point(sphere->get_center());
 
   LPoint3 into_center(get_center());
   PN_stdfloat into_radius(get_radius());
 
-  LVector3 from_radius_v =
-    LVector3(sphere->get_radius(), 0.0f, 0.0f) * wrt_mat;
+  LVector3 from_radius_v = wrt_space.xform_vec(LVector3(sphere->get_radius(), 0.0f, 0.0f));
   PN_stdfloat from_radius = length(from_radius_v);
 
   LPoint3 into_intersection_point(from_b);
@@ -150,8 +147,8 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   if (dist2 > (into_radius + from_radius) * (into_radius + from_radius)) {
     // No intersection with the current position.  Check the delta from the
     // previous frame.
-    CPT(TransformState) wrt_prev_space = entry.get_wrt_prev_space();
-    LPoint3 from_a = sphere->get_center() * wrt_prev_space->get_mat();
+    Transform wrt_prev_space = entry.get_wrt_prev_space();
+    LPoint3 from_a = wrt_prev_space.xform_point(sphere->get_center());
 
     if (!from_a.almost_equal(from_b)) {
       LVector3 from_direction = from_b - from_a;
@@ -192,7 +189,7 @@ test_intersection_from_sphere(const CollisionEntry &entry) const {
   }
   PT(CollisionEntry) new_entry = new CollisionEntry(entry);
 
-  LPoint3 from_center = sphere->get_center() * wrt_mat;
+  LPoint3 from_center = wrt_space.xform_point(sphere->get_center());
 
   LVector3 surface_normal;
   LVector3 v(into_intersection_point - into_center);
@@ -603,7 +600,7 @@ fill_viz_geom() {
   PT(Geom) ring_geom = new Geom(vdata);
   ring_geom->add_primitive(ring);
 
-  CPT(TransformState) transform = TransformState::make_pos(get_center());
+  Transform transform = Transform::make_pos(get_center());
 
   _viz_geom->add_geom(geom, get_solid_viz_state());
   _viz_geom->add_geom(ring_geom, get_wireframe_viz_state());

+ 3 - 3
panda/src/collide/collisionVisualizer.cxx

@@ -131,9 +131,9 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
     // We don't want to inherit the transform from above!  We ignore whatever
     // transforms were above the CollisionVisualizer node; it always renders
     // its objects according to their appropriate net transform.
-    xform_data._net_transform = TransformState::make_identity();
+    xform_data._net_transform = Transform::make_identity();
     xform_data._view_frustum = trav->get_view_frustum();
-    xform_data.apply_transform(net_transform);
+    xform_data.apply_transform(Transform::make_mat(net_transform->get_mat()));
 
     // Draw all the collision solids.
     Solids::const_iterator si;
@@ -284,7 +284,7 @@ collision_tested(const CollisionEntry &entry, bool detected) {
   CollisionRecorder::collision_tested(entry, detected);
 
   NodePath node_path = entry.get_into_node_path();
-  CPT(TransformState) net_transform = node_path.get_net_transform();
+  CPT(TransformState) net_transform = TransformState::make_mat(node_path.get_net_transform().get_mat());
   CPT(CollisionSolid) solid = entry.get_into();
   nassertv(!solid.is_null());
 

+ 1 - 2
panda/src/cull/cullBinBackToFront.cxx

@@ -60,8 +60,7 @@ add_object(CullableObject *object, Thread *current_thread) {
   nassertv(gbv != nullptr);
 
   LPoint3 center = gbv->get_approx_center();
-  nassertv(object->_internal_transform != nullptr);
-  center = center * object->_internal_transform->get_mat();
+  center = object->_internal_transform.xform_point(center);
 
   PN_stdfloat distance = _gsg->compute_distance_to(center);
   _objects.push_back(ObjectData(object, distance));

+ 1 - 2
panda/src/cull/cullBinFrontToBack.cxx

@@ -60,8 +60,7 @@ add_object(CullableObject *object, Thread *current_thread) {
   nassertv(gbv != nullptr);
 
   LPoint3 center = gbv->get_approx_center();
-  nassertv(object->_internal_transform != nullptr);
-  center = center * object->_internal_transform->get_mat();
+  center = object->_internal_transform.xform_point(center);
 
   PN_stdfloat distance = _gsg->compute_distance_to(center);
   _objects.push_back(ObjectData(object, distance));

+ 1 - 5
panda/src/cull/cullBinStateSorted.I

@@ -61,9 +61,5 @@ operator < (const ObjectData &other) const {
   }
 
   // Uniform updates are actually pretty fast.
-  if (_object->_internal_transform != other._object->_internal_transform) {
-    return _object->_internal_transform < other._object->_internal_transform;
-  }
-
-  return 0;
+  return _object->_internal_transform.compare_to(other._object->_internal_transform) < 0;
 }

+ 14 - 14
panda/src/display/graphicsEngine.cxx

@@ -33,7 +33,7 @@
 #include "string_utils.h"
 #include "geomCacheManager.h"
 #include "renderState.h"
-#include "transformState.h"
+#include "transform.h"
 #include "thread.h"
 #include "pipeline.h"
 #include "throw_event.h"
@@ -1206,7 +1206,7 @@ dispatch_compute(const LVecBase3i &work_groups, const RenderState *state, Graphi
   if (draw_name.empty()) {
     // A single-threaded environment.  No problem.
     gsg->push_group_marker(std::string("Compute ") + shader->get_filename(Shader::ST_compute).get_basename());
-    gsg->set_state_and_transform(state, TransformState::make_identity());
+    gsg->set_state_and_transform(state, Transform::make_identity());
     gsg->dispatch_compute(work_groups[0], work_groups[1], work_groups[2]);
     gsg->pop_group_marker();
 
@@ -2017,10 +2017,10 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
   // the parent of the scene_root is the empty NodePath, although it need not
   // be.)
   NodePath scene_parent = scene_root.get_parent(current_thread);
-  CPT(TransformState) camera_transform = camera.get_transform(scene_parent, current_thread);
-  CPT(TransformState) world_transform = scene_parent.get_transform(camera, current_thread);
+  Transform camera_transform = camera.get_transform(scene_parent, current_thread);
+  Transform world_transform = scene_parent.get_transform(camera, current_thread);
 
-  if (camera_transform->is_invalid()) {
+  /*if (camera_transform.is_invalid()) {
     // There must be a singular transform over the scene.
     if (!_singular_warning_last_frame) {
       display_cat.warning()
@@ -2029,9 +2029,9 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
       _singular_warning_this_frame = true;
     }
     return nullptr;
-  }
+  }*/
 
-  if (world_transform->is_invalid()) {
+  /*if (world_transform.is_invalid()) {
     // There must be a singular transform over the camera.
     if (!_singular_warning_last_frame) {
       display_cat.warning()
@@ -2040,7 +2040,7 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
     }
     _singular_warning_this_frame = true;
     return nullptr;
-  }
+  }*/
 
   CPT(RenderState) initial_state = camera_node->get_initial_state();
 
@@ -2065,10 +2065,10 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
   scene_setup->set_camera_transform(camera_transform);
   scene_setup->set_world_transform(world_transform);
 
-  CPT(TransformState) cs_transform = gsg->get_cs_transform_for(lens->get_coordinate_system());
+  Transform cs_transform = gsg->get_cs_transform_for(lens->get_coordinate_system());
   scene_setup->set_cs_transform(cs_transform);
 
-  CPT(TransformState) cs_world_transform = cs_transform->compose(world_transform);
+  Transform cs_world_transform = cs_transform.compose(world_transform);
   scene_setup->set_cs_world_transform(cs_world_transform);
 
   if (view_frustum_cull) {
@@ -2086,9 +2086,9 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
       local_frustum = bv->make_copy()->as_geometric_bounding_volume();
       nassertr(!local_frustum.is_null(), nullptr);
 
-      CPT(TransformState) cull_center_transform =
+      Transform cull_center_transform =
         scene_setup->get_cull_center().get_transform(scene_parent, current_thread);
-      local_frustum->xform(cull_center_transform->get_mat());
+      local_frustum->xform(cull_center_transform.get_mat());
 
       scene_setup->set_view_frustum(local_frustum);
     }
@@ -2137,7 +2137,7 @@ do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thre
     static CPT(RenderState) state = RenderState::make(
       DepthTestAttrib::make(DepthTestAttrib::M_none));
     gsg->clear_before_callback();
-    gsg->set_state_and_transform(state, TransformState::make_identity());
+    gsg->set_state_and_transform(state, Transform::make_identity());
 
     DisplayRegionDrawCallbackData cbdata(cull_result, scene_setup);
     cbobj->do_callback(&cbdata);
@@ -2810,7 +2810,7 @@ thread_main() {
         const ShaderAttrib *sattr;
         _state->get_attrib(sattr);
         _gsg->push_group_marker(std::string("Compute ") + sattr->get_shader()->get_filename(Shader::ST_compute).get_basename());
-        _gsg->set_state_and_transform(_state, TransformState::make_identity());
+        _gsg->set_state_and_transform(_state, Transform::make_identity());
         _gsg->dispatch_compute(_work_groups[0], _work_groups[1], _work_groups[2]);
         _gsg->pop_group_marker();
       }

+ 4 - 4
panda/src/display/graphicsStateGuardian.I

@@ -857,9 +857,9 @@ mark_new() {
  * when geometry is about to be rendered.  Therefore, this "get" function is
  * typically only meaningful during the geometry rendering process.
  */
-INLINE CPT(TransformState) GraphicsStateGuardian::
+INLINE Transform GraphicsStateGuardian::
 get_external_transform() const {
-  return _inv_cs_transform->compose(_internal_transform);
+  return _inv_cs_transform.compose(_internal_transform);
 }
 
 /**
@@ -867,7 +867,7 @@ get_external_transform() const {
  * when geometry is about to be rendered.  Therefore, this "get" function is
  * typically only meaningful during the geometry rendering process.
  */
-INLINE CPT(TransformState) GraphicsStateGuardian::
+INLINE Transform GraphicsStateGuardian::
 get_internal_transform() const {
   return _internal_transform;
 }
@@ -911,7 +911,7 @@ get_current_lens() const {
 /**
  * Returns the inverse of the transform returned by get_cs_transform().
  */
-INLINE CPT(TransformState) GraphicsStateGuardian::
+INLINE Transform GraphicsStateGuardian::
 get_inv_cs_transform() const {
   return _inv_cs_transform;
 }

+ 111 - 105
panda/src/display/graphicsStateGuardian.cxx

@@ -154,7 +154,7 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _engine(engine)
 {
   _coordinate_system = CS_invalid;
-  _internal_transform = TransformState::make_identity();
+  _internal_transform = Transform::make_identity();
 
   if (_internal_coordinate_system == CS_default) {
     _internal_coordinate_system = get_default_coordinate_system();
@@ -167,8 +167,8 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _current_stereo_channel = Lens::SC_mono;
   _current_tex_view_offset = 0;
   _current_lens = nullptr;
-  _projection_mat = TransformState::make_identity();
-  _projection_mat_inv = TransformState::make_identity();
+  _projection_mat = LMatrix4::ident_mat();
+  _projection_mat_inv = LMatrix4::ident_mat();
 
   _needs_reset = true;
   _is_valid = false;
@@ -380,16 +380,16 @@ set_coordinate_system(CoordinateSystem cs) {
   // Changing the external coordinate system changes the cs_transform.
   if (_internal_coordinate_system == CS_default ||
       _internal_coordinate_system == _coordinate_system) {
-    _cs_transform = TransformState::make_identity();
-    _inv_cs_transform = TransformState::make_identity();
+    _cs_transform = Transform::make_identity();
+    _inv_cs_transform = Transform::make_identity();
 
   } else {
     _cs_transform =
-      TransformState::make_mat
+      Transform::make_mat
       (LMatrix4::convert_mat(_coordinate_system,
                               _internal_coordinate_system));
     _inv_cs_transform =
-      TransformState::make_mat
+      Transform::make_mat
       (LMatrix4::convert_mat(_internal_coordinate_system,
                               _coordinate_system));
   }
@@ -526,11 +526,12 @@ set_scene(SceneSetup *scene_setup) {
 
   set_coordinate_system(_current_lens->get_coordinate_system());
 
-  _projection_mat = calc_projection_mat(_current_lens);
-  if (_projection_mat == nullptr) {
+  if (!calc_projection_mat(_projection_mat, _current_lens)) {
+    return false;
+  }
+  if (!_projection_mat_inv.invert_from(_projection_mat)) {
     return false;
   }
-  _projection_mat_inv = _projection_mat->get_inverse();
   return prepare_lens();
 }
 
@@ -1236,8 +1237,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     DCAST_INTO_V(lt, np.node());
     LColor const &c = lt->get_color();
     LColor const &s = lt->get_specular_color();
-    LMatrix4 t = np.get_net_transform()->get_mat() *
-                 _scene_setup->get_world_transform()->get_mat();
+    LMatrix4 t = np.get_net_transform().get_mat() *
+                 _scene_setup->get_world_transform().get_mat();
     LVecBase3 d = -(t.xform_vec(lt->get_direction()));
     d.normalize();
     LVecBase3 h = d + LVecBase3(0,-1,0);
@@ -1256,8 +1257,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     DCAST_INTO_V(lt, np.node());
     LColor const &c = lt->get_color();
     LColor const &s = lt->get_specular_color();
-    LMatrix4 t = np.get_net_transform()->get_mat() *
-                 _scene_setup->get_world_transform()->get_mat();
+    LMatrix4 t = np.get_net_transform().get_mat() *
+                 _scene_setup->get_world_transform().get_mat();
     LVecBase3 p = (t.xform_point(lt->get_point()));
     LVecBase3 a = lt->get_attenuation();
     Lens *lens = lt->get_lens(0);
@@ -1280,8 +1281,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     LColor const &c = lt->get_color();
     LColor const &s = lt->get_specular_color();
     PN_stdfloat cutoff = ccos(deg_2_rad(lens->get_hfov() * 0.5f));
-    LMatrix4 t = np.get_net_transform()->get_mat() *
-                 _scene_setup->get_world_transform()->get_mat();
+    LMatrix4 t = np.get_net_transform().get_mat() *
+                 _scene_setup->get_world_transform().get_mat();
     LVecBase3 p = t.xform_point(lens->get_nodal_point());
     LVecBase3 d = -(t.xform_vec(lens->get_view_vector()));
     into[0].set(c[0], c[1], c[2], c[3]);
@@ -1439,10 +1440,10 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     DCAST_INTO_V(plane_node, np.node());
 
     // Transform plane to world space
-    CPT(TransformState) transform = np.get_net_transform();
+    Transform transform = np.get_net_transform();
     LPlane plane = plane_node->get_plane();
-    if (!transform->is_identity()) {
-      plane.xform(transform->get_mat());
+    if (!transform.is_identity()) {
+      plane.xform(transform.get_mat());
     }
     into[0] = LCAST(float, plane);
     return;
@@ -1459,11 +1460,11 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       const PlaneNode *plane_node;
       DCAST_INTO_V(plane_node, plane.node());
 
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(
+      Transform transform =
+        _scene_setup->get_cs_world_transform().compose(
           plane.get_transform(_scene_setup->get_scene_root().get_parent()));
 
-      LPlane xformed_plane = plane_node->get_plane() * transform->get_mat();
+      LPlane xformed_plane = plane_node->get_plane() * transform.get_mat();
       into[i] = LCAST(float, xformed_plane);
     }
 
@@ -1488,35 +1489,35 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     return;
   }
   case Shader::SMO_world_to_view: {
-    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_world_transform()->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_world_transform().get_mat());
     return;
   }
   case Shader::SMO_view_to_world: {
-    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform()->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform().get_mat());
     return;
   }
   case Shader::SMO_model_to_view: {
-    *(LMatrix4f *)into = LCAST(float, _inv_cs_transform->compose(_internal_transform)->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _inv_cs_transform.compose(_internal_transform).get_mat());
     return;
   }
   case Shader::SMO_model_to_apiview: {
-    *(LMatrix4f *)into = LCAST(float, _internal_transform->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _internal_transform.get_mat());
     return;
   }
   case Shader::SMO_view_to_model: {
-    *(LMatrix4f *)into = LCAST(float, _internal_transform->invert_compose(_cs_transform)->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _internal_transform.invert_compose(_cs_transform).get_mat());
     return;
   }
   case Shader::SMO_apiview_to_model: {
-    *(LMatrix4f *)into = LCAST(float, _internal_transform->get_inverse()->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _internal_transform.get_inverse().get_mat());
     return;
   }
   case Shader::SMO_apiview_to_view: {
-    *(LMatrix4f *)into = LCAST(float, _inv_cs_transform->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _inv_cs_transform.get_mat());
     return;
   }
   case Shader::SMO_view_to_apiview: {
-    *(LMatrix4f *)into = LCAST(float, _cs_transform->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _cs_transform.get_mat());
     return;
   }
   case Shader::SMO_clip_to_view: {
@@ -1540,48 +1541,48 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     return;
   }
   case Shader::SMO_apiclip_to_view: {
-    *(LMatrix4f *)into = LCAST(float, _projection_mat_inv->get_mat() * _inv_cs_transform->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _projection_mat_inv * _inv_cs_transform.get_mat());
     return;
   }
   case Shader::SMO_view_to_apiclip: {
-    *(LMatrix4f *)into = LCAST(float, _cs_transform->get_mat() * _projection_mat->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _cs_transform.get_mat() * _projection_mat);
     return;
   }
   case Shader::SMO_apiclip_to_apiview: {
-    *(LMatrix4f *)into = LCAST(float, _projection_mat_inv->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _projection_mat_inv);
     return;
   }
   case Shader::SMO_apiview_to_apiclip: {
-    *(LMatrix4f *)into = LCAST(float, _projection_mat->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _projection_mat);
     return;
   }
   case Shader::SMO_view_x_to_view: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertv(!np.is_empty());
-    *(LMatrix4f *)into = LCAST(float, np.get_net_transform()->get_mat() *
-      _scene_setup->get_world_transform()->get_mat());
+    *(LMatrix4f *)into = LCAST(float, np.get_net_transform().get_mat() *
+      _scene_setup->get_world_transform().get_mat());
     return;
   }
   case Shader::SMO_view_to_view_x: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertv(!np.is_empty());
-    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform()->get_mat() *
-      np.get_net_transform()->get_inverse()->get_mat());
+    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform().get_mat() *
+      np.get_net_transform().get_inverse().get_mat());
     return;
   }
   case Shader::SMO_apiview_x_to_view: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertv(!np.is_empty());
     *(LMatrix4f *)into = LCAST(float, LMatrix4::convert_mat(_internal_coordinate_system, _coordinate_system) *
-      np.get_net_transform()->get_mat() *
-      _scene_setup->get_world_transform()->get_mat());
+      np.get_net_transform().get_mat() *
+      _scene_setup->get_world_transform().get_mat());
     return;
   }
   case Shader::SMO_view_to_apiview_x: {
     const NodePath &np = _target_shader->get_shader_input_nodepath(name);
     nassertv(!np.is_empty());
-    *(LMatrix4f *)into = LCAST(float, (_scene_setup->get_camera_transform()->get_mat() *
-         np.get_net_transform()->get_inverse()->get_mat() *
+    *(LMatrix4f *)into = LCAST(float, (_scene_setup->get_camera_transform().get_mat() *
+         np.get_net_transform().get_inverse().get_mat() *
          LMatrix4::convert_mat(_coordinate_system, _internal_coordinate_system)));
     return;
   }
@@ -1593,8 +1594,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     const Lens *lens = node->get_lens();
     *(LMatrix4f *)into = LCAST(float, lens->get_projection_mat_inv(_current_stereo_channel) *
       LMatrix4::convert_mat(lens->get_coordinate_system(), _coordinate_system) *
-      np.get_net_transform()->get_mat() *
-      _scene_setup->get_world_transform()->get_mat());
+      np.get_net_transform().get_mat() *
+      _scene_setup->get_world_transform().get_mat());
     return;
   }
   case Shader::SMO_view_to_clip_x: {
@@ -1603,8 +1604,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     const LensNode *node;
     DCAST_INTO_V(node, np.node());
     const Lens *lens = node->get_lens();
-    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform()->get_mat() *
-      np.get_net_transform()->get_inverse()->get_mat() *
+    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform().get_mat() *
+      np.get_net_transform().get_inverse().get_mat() *
       LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system()) *
       lens->get_projection_mat(_current_stereo_channel));
     return;
@@ -1615,10 +1616,14 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     const LensNode *node;
     DCAST_INTO_V(node, np.node());
     const Lens *lens = node->get_lens();
-    *(LMatrix4f *)into = LCAST(float, calc_projection_mat(lens)->get_inverse()->get_mat() *
-      get_cs_transform_for(lens->get_coordinate_system())->get_inverse()->get_mat() *
-      np.get_net_transform()->get_mat() *
-      _scene_setup->get_world_transform()->get_mat());
+    LMatrix4 proj_mat;
+    calc_projection_mat(proj_mat, lens);
+    LMatrix4 proj_mat_inv;
+    proj_mat_inv.invert_from(proj_mat);
+    *(LMatrix4f *)into = LCAST(float, proj_mat_inv *
+      get_cs_transform_for(lens->get_coordinate_system()).get_inverse().get_mat() *
+      np.get_net_transform().get_mat() *
+      _scene_setup->get_world_transform().get_mat());
     return;
   }
   case Shader::SMO_view_to_apiclip_x: {
@@ -1627,10 +1632,12 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
     const LensNode *node;
     DCAST_INTO_V(node, np.node());
     const Lens *lens = node->get_lens();
-    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform()->get_mat() *
-      np.get_net_transform()->get_inverse()->get_mat() *
-      get_cs_transform_for(lens->get_coordinate_system())->get_mat() *
-      calc_projection_mat(lens)->get_mat());
+    LMatrix4 proj_mat;
+    calc_projection_mat(proj_mat, lens);
+    *(LMatrix4f *)into = LCAST(float, _scene_setup->get_camera_transform().get_mat() *
+      np.get_net_transform().get_inverse().get_mat() *
+      get_cs_transform_for(lens->get_coordinate_system()).get_mat() *
+      proj_mat);
     return;
   }
   case Shader::SMO_mat_constant_x_attrib: {
@@ -1656,9 +1663,9 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       const LensNode *lnode = (const LensNode *)node;
       const Lens *lens = lnode->get_lens();
 
-      LMatrix4 t = _inv_cs_transform->get_mat() *
-        _scene_setup->get_camera_transform()->get_mat() *
-        np.get_net_transform()->get_inverse()->get_mat() *
+      LMatrix4 t = _inv_cs_transform.get_mat() *
+        _scene_setup->get_camera_transform().get_mat() *
+        np.get_net_transform().get_inverse().get_mat() *
         LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system());
 
       if (!lnode->is_of_type(PointLight::get_class_type())) {
@@ -1731,9 +1738,9 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       DCAST_INTO_V(lnode, light.node());
       Lens *lens = lnode->get_lens();
 
-      LMatrix4 t = _inv_cs_transform->get_mat() *
-        _scene_setup->get_camera_transform()->get_mat() *
-        light.get_net_transform()->get_inverse()->get_mat() *
+      LMatrix4 t = _inv_cs_transform.get_mat() *
+        _scene_setup->get_camera_transform().get_mat() *
+        light.get_net_transform().get_inverse().get_mat() *
         LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system());
 
       if (!lnode->is_of_type(PointLight::get_class_type())) {
@@ -1767,8 +1774,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       into[0] = LCAST(float, light->get_color());
       into[1] = LVecBase4f(LCAST(float, light->get_attenuation()), 0);
 
-      LMatrix4 mat = np.get_net_transform()->get_mat() *
-        _scene_setup->get_world_transform()->get_mat();
+      LMatrix4 mat = np.get_net_transform().get_mat() *
+        _scene_setup->get_world_transform().get_mat();
 
       if (node->is_of_type(DirectionalLight::get_class_type())) {
         LVecBase3 d = mat.xform_vec(((const DirectionalLight *)node)->get_direction());
@@ -1831,8 +1838,8 @@ fetch_specified_part(Shader::ShaderMatInput part, InternalName *name,
       LVecBase2i pixel_size = _current_display_region->get_pixel_size();
 
       LVector3 height(0.0f, thickness, 1.0f);
-      height = height * _projection_mat->get_mat();
-      height = height * _internal_transform->get_scale()[1];
+      height = height * _projection_mat;
+      height = height * _internal_transform.get_scale()[1];
       PN_stdfloat s = height[1] * pixel_size[1];
 
       if (_current_lens->is_orthographic()) {
@@ -1938,9 +1945,9 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LVecBase4f &
       DirectionalLight *light;
       DCAST_INTO_V(light, node);
 
-      CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
-      LVector3 dir = -(light->get_direction() * transform->get_mat());
-      dir *= _scene_setup->get_cs_world_transform()->get_mat();
+      Transform transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
+      LVector3 dir = -transform.xform_vec(light->get_direction());
+      dir = _scene_setup->get_cs_world_transform().xform_vec(dir);
       v.set(dir[0], dir[1], dir[2], 0);
     }
     else {
@@ -1949,12 +1956,11 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LVecBase4f &
       Lens *lens = light->get_lens();
       nassertv(lens != nullptr);
 
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(
+      Transform transform =
+        _scene_setup->get_cs_world_transform().compose(
           np.get_transform(_scene_setup->get_scene_root().get_parent()));
 
-      const LMatrix4 &light_mat = transform->get_mat();
-      LPoint3 pos = lens->get_nodal_point() * light_mat;
+      LPoint3 pos = transform.xform_point(lens->get_nodal_point());
       v.set(pos[0], pos[1], pos[2], 1);
     }
   }
@@ -1970,9 +1976,9 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LVecBase4f &
       DirectionalLight *light;
       DCAST_INTO_V(light, node);
 
-      CPT(TransformState) transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
-      LVector3 dir = -(light->get_direction() * transform->get_mat());
-      dir *= _scene_setup->get_cs_world_transform()->get_mat();
+      Transform transform = np.get_transform(_scene_setup->get_scene_root().get_parent());
+      LVector3 dir = -transform.xform_vec(light->get_direction());
+      dir *= _scene_setup->get_cs_world_transform().get_mat();
       dir.normalize();
       dir += LVector3(0, 0, 1);
       dir.normalize();
@@ -1984,12 +1990,11 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LVecBase4f &
       Lens *lens = light->get_lens();
       nassertv(lens != nullptr);
 
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(
+      Transform transform =
+        _scene_setup->get_cs_world_transform().compose(
           np.get_transform(_scene_setup->get_scene_root().get_parent()));
 
-      const LMatrix4 &light_mat = transform->get_mat();
-      LPoint3 pos = lens->get_nodal_point() * light_mat;
+      LPoint3 pos = transform.xform_point(lens->get_nodal_point());
       pos.normalize();
       pos += LVector3(0, 0, 1);
       pos.normalize();
@@ -2010,12 +2015,11 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LVecBase4f &
       Lens *lens = light->get_lens();
       nassertv(lens != nullptr);
 
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(
+      Transform transform =
+        _scene_setup->get_cs_world_transform().compose(
           np.get_transform(_scene_setup->get_scene_root().get_parent()));
 
-      const LMatrix4 &light_mat = transform->get_mat();
-      LVector3 dir = lens->get_view_vector() * light_mat;
+      LVector3 dir = transform.xform_vec(lens->get_view_vector());
       v.set(dir[0], dir[1], dir[2], 0);
     }
   }
@@ -2149,11 +2153,10 @@ fetch_specified_light(const NodePath &np, LVecBase4f *into) {
       into[Shader::LA_ambient].set(0, 0, 0, 1);
       into[Shader::LA_diffuse] = color;
 
-      CPT(TransformState) net_transform =
+      Transform net_transform =
         np.get_transform(_scene_setup->get_scene_root().get_parent());
-      CPT(TransformState) transform =
-        _scene_setup->get_cs_world_transform()->compose(net_transform);
-      const LMatrix4 &light_mat = transform->get_mat();
+      Transform transform =
+        _scene_setup->get_cs_world_transform().compose(net_transform);
 
       LightLensNode *light;
       DCAST_INTO_V(light, node);
@@ -2164,7 +2167,7 @@ fetch_specified_light(const NodePath &np, LVecBase4f *into) {
         DirectionalLight *light;
         DCAST_INTO_V(light, node);
 
-        LVector3 dir = -(light->get_direction() * light_mat);
+        LVector3 dir = -transform.xform_vec(light->get_direction());
         into[Shader::LA_position].set(dir[0], dir[1], dir[2], 0);
 
         dir.normalize();
@@ -2173,7 +2176,7 @@ fetch_specified_light(const NodePath &np, LVecBase4f *into) {
         into[Shader::LA_half_vector].set(dir[0], dir[1], dir[2], 1);
       }
       else {
-        LPoint3 pos = lens->get_nodal_point() * light_mat;
+        LPoint3 pos = transform.xform_point(lens->get_nodal_point());
         into[Shader::LA_position].set(pos[0], pos[1], pos[2], 1);
 
         pos.normalize();
@@ -2190,12 +2193,12 @@ fetch_specified_light(const NodePath &np, LVecBase4f *into) {
         into[Shader::LA_spot_params].set(-1, 180, light->get_exponent(), 0);
       }
 
-      LVector3 dir = lens->get_view_vector() * light_mat;
+      LVector3 dir = transform.xform_vec(lens->get_view_vector());
       into[Shader::LA_spot_direction].set(dir[0], dir[1], dir[2], 0);
 
-      LMatrix4 t = _inv_cs_transform->get_mat() *
-        _scene_setup->get_camera_transform()->get_mat() *
-        net_transform->get_inverse()->get_mat() *
+      LMatrix4 t = _inv_cs_transform.get_mat() *
+        _scene_setup->get_camera_transform().get_mat() *
+        net_transform.get_inverse().get_mat() *
         LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system());
 
       if (!node->is_of_type(PointLight::get_class_type())) {
@@ -2633,21 +2636,24 @@ prepare_lens() {
 }
 
 /**
- * Given a lens, this function calculates the appropriate projection matrix
- * for this gsg.  The result depends on the peculiarities of the rendering
- * API.
+ * Given a lens, calculates the appropriate projection matrix for use with
+ * this gsg.  Note that the projection matrix depends a lot upon the
+ * coordinate system of the rendering API.
+ *
+ * The return value is true if the lens is acceptable, false if it is not.
  */
-CPT(TransformState) GraphicsStateGuardian::
-calc_projection_mat(const Lens *lens) {
+bool GraphicsStateGuardian::
+calc_projection_mat(LMatrix4 &mat, const Lens *lens) {
   if (lens == nullptr) {
-    return nullptr;
+    return false;
   }
 
   if (!lens->is_linear()) {
-    return nullptr;
+    return false;
   }
 
-  return TransformState::make_identity();
+  mat = LMatrix4::ident_mat();
+  return true;
 }
 
 /**
@@ -3017,7 +3023,7 @@ reset() {
  */
 void GraphicsStateGuardian::
 set_state_and_transform(const RenderState *state,
-                        const TransformState *trans) {
+                        const Transform &trans) {
 }
 
 /**
@@ -3047,7 +3053,7 @@ get_render_buffer(int buffer_type, const FrameBufferProperties &prop) {
  * set_coordinate_system(cs).  This is another way of saying the cs_transform
  * when rendering the scene for a camera with the indicated coordinate system.
  */
-CPT(TransformState) GraphicsStateGuardian::
+Transform GraphicsStateGuardian::
 get_cs_transform_for(CoordinateSystem cs) const {
   if (_coordinate_system == cs) {
     // We've already calculated this.
@@ -3055,10 +3061,10 @@ get_cs_transform_for(CoordinateSystem cs) const {
 
   } else if (_internal_coordinate_system == CS_default ||
              _internal_coordinate_system == cs) {
-    return TransformState::make_identity();
+    return Transform::make_identity();
 
   } else {
-    return TransformState::make_mat
+    return Transform::make_mat
       (LMatrix4::convert_mat(cs, _internal_coordinate_system));
   }
 }
@@ -3069,7 +3075,7 @@ get_cs_transform_for(CoordinateSystem cs) const {
  * (as returned by get_internal_coordinate_system()).  This is used for
  * rendering.
  */
-CPT(TransformState) GraphicsStateGuardian::
+Transform GraphicsStateGuardian::
 get_cs_transform() const {
   return _cs_transform;
 }

+ 12 - 12
panda/src/display/graphicsStateGuardian.h

@@ -330,7 +330,7 @@ public:
                                           Thread *current_thread);
 
   virtual void set_state_and_transform(const RenderState *state,
-                                       const TransformState *transform);
+                                       const Transform &transform);
 
   PN_stdfloat compute_distance_to(const LPoint3 &point) const;
 
@@ -355,7 +355,7 @@ public:
 
   virtual void remove_window(GraphicsOutputBase *window);
 
-  virtual CPT(TransformState) calc_projection_mat(const Lens *lens);
+  virtual bool calc_projection_mat(LMatrix4 &mat, const Lens *lens);
   virtual bool prepare_lens();
 
   virtual bool begin_frame(Thread *current_thread);
@@ -404,8 +404,8 @@ public:
   INLINE void mark_new();
   virtual void reset();
 
-  INLINE CPT(TransformState) get_external_transform() const;
-  INLINE CPT(TransformState) get_internal_transform() const;
+  INLINE Transform get_external_transform() const;
+  INLINE Transform get_internal_transform() const;
 
   RenderBuffer get_render_buffer(int buffer_type, const FrameBufferProperties &prop);
 
@@ -414,9 +414,9 @@ public:
   INLINE int get_current_tex_view_offset() const;
   INLINE const Lens *get_current_lens() const;
 
-  virtual CPT(TransformState) get_cs_transform_for(CoordinateSystem cs) const;
-  virtual CPT(TransformState) get_cs_transform() const;
-  INLINE CPT(TransformState) get_inv_cs_transform() const;
+  virtual Transform get_cs_transform_for(CoordinateSystem cs) const;
+  virtual Transform get_cs_transform() const;
+  INLINE Transform get_inv_cs_transform() const;
 
   void do_issue_clip_plane();
   void do_issue_color();
@@ -499,7 +499,7 @@ protected:
   RenderState::SlotMask _inv_state_mask;
 
   // The current transform, as of the last call to set_state_and_transform().
-  CPT(TransformState) _internal_transform;
+  Transform _internal_transform;
 
   // The current TextureAttrib is a special case; we may further restrict it
   // (according to graphics cards limits) or extend it (according to
@@ -525,14 +525,14 @@ protected:
   Lens::StereoChannel _current_stereo_channel;
   int _current_tex_view_offset;
   CPT(Lens) _current_lens;
-  CPT(TransformState) _projection_mat;
-  CPT(TransformState) _projection_mat_inv;
+  LMatrix4 _projection_mat;
+  LMatrix4 _projection_mat_inv;
   const FrameBufferProperties *_current_properties;
 
   CoordinateSystem _coordinate_system;
   CoordinateSystem _internal_coordinate_system;
-  CPT(TransformState) _cs_transform;
-  CPT(TransformState) _inv_cs_transform;
+  Transform _cs_transform;
+  Transform _inv_cs_transform;
 
   LColor _scene_graph_color;
   bool _has_scene_graph_color;

+ 9 - 9
panda/src/distort/projectionScreen.cxx

@@ -163,7 +163,7 @@ generate_screen(const NodePath &projector, const std::string &screen_name,
   // First, get the relative coordinate space of the projector.
   LMatrix4 rel_mat;
   NodePath this_np(this);
-  rel_mat = projector.get_transform(this_np)->get_mat();
+  rel_mat = projector.get_transform(this_np).get_mat();
 
   // Create a GeomNode to hold this mesh.
   PT(GeomNode) geom_node = new GeomNode(screen_name);
@@ -327,8 +327,8 @@ recompute_if_stale(const NodePath &this_np) {
 
     } else {
       // Get the relative transform to ensure it hasn't changed.
-      CPT(TransformState) transform = this_np.get_transform(_projector);
-      const LMatrix4 &top_mat = transform->get_mat();
+      Transform transform = this_np.get_transform(_projector);
+      LMatrix4 top_mat = transform.get_mat();
       if (!_rel_top_mat.almost_equal(top_mat)) {
         _rel_top_mat = top_mat;
         _computed_rel_top_mat = true;
@@ -401,8 +401,8 @@ recompute_child(const WorkingNodePath &np, LMatrix4 &rel_mat,
                 bool &computed_rel_mat) {
   PandaNode *child = np.node();
 
-  const TransformState *transform = child->get_transform();
-  if (!transform->is_identity()) {
+  Transform transform;
+  if (child->get_transform(transform)) {
     // This child node has a transform; therefore, we must recompute the
     // relative matrix from this point.
     LMatrix4 new_rel_mat;
@@ -432,7 +432,7 @@ recompute_geom_node(const WorkingNodePath &np, LMatrix4 &rel_mat,
   if (!computed_rel_mat) {
     // All right, time to compute the matrix.
     NodePath true_np = np.get_node_path();
-    rel_mat = true_np.get_transform(_projector)->get_mat();
+    rel_mat = true_np.get_transform(_projector).get_mat();
     computed_rel_mat = true;
 
     if (distort_cat.is_spam()) {
@@ -611,8 +611,8 @@ make_mesh_children(PandaNode *new_node, const WorkingNodePath &np,
     PandaNode *child = node->get_child(i);
     PandaNode *new_child;
 
-    const TransformState *transform = child->get_transform();
-    if (!transform->is_identity()) {
+    Transform transform;
+    if (child->get_transform(transform)) {
       // This child node has a transform; therefore, we must recompute the
       // relative matrix from this point.
       LMatrix4 new_rel_mat;
@@ -648,7 +648,7 @@ make_mesh_geom_node(const WorkingNodePath &np, const NodePath &camera,
   if (!computed_rel_mat) {
     // All right, time to compute the matrix.
     NodePath true_np = np.get_node_path();
-    rel_mat = true_np.get_transform(camera)->get_mat();
+    rel_mat = true_np.get_transform(camera).get_mat();
     computed_rel_mat = true;
   }
 

+ 14 - 16
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -921,17 +921,16 @@ prepare_display_region(DisplayRegionPipelineReader *dr) {
  * this gsg.  Note that the projection matrix depends a lot upon the
  * coordinate system of the rendering API.
  *
- * The return value is a TransformState if the lens is acceptable, NULL if it
- * is not.
+ * The return value is true if the lens is acceptable, false if it is not.
  */
-CPT(TransformState) DXGraphicsStateGuardian9::
-calc_projection_mat(const Lens *lens) {
+bool DXGraphicsStateGuardian9::
+calc_projection_mat(LMatrix4 &result, const Lens *lens) {
   if (lens == nullptr) {
-    return nullptr;
+    return false;
   }
 
   if (!lens->is_linear()) {
-    return nullptr;
+    return false;
   }
 
   // DirectX also uses a Z range of 0 to 1, whereas the Panda convention is
@@ -943,7 +942,7 @@ calc_projection_mat(const Lens *lens) {
      0, 0, 0.5, 0,
      0, 0, 0.5, 1);
 
-  LMatrix4 result =
+  result =
     LMatrix4::convert_mat(CS_yup_left, _current_lens->get_coordinate_system()) *
     lens->get_projection_mat(_current_stereo_channel) *
     rescale_mat;
@@ -954,7 +953,7 @@ calc_projection_mat(const Lens *lens) {
     result *= LMatrix4::scale_mat(1.0f, -1.0f, 1.0f);
   }
 
-  return TransformState::make_mat(result);
+  return true;
 }
 
 /**
@@ -2281,9 +2280,9 @@ reset() {
   HRESULT hr;
 
   // make sure gsg passes all current state down to us
-  // set_state_and_transform(RenderState::make_empty(),
-  // TransformState::make_identity()); want gsg to pass all state settings
-  // down so any non-matching defaults we set here get overwritten
+  //set_state_and_transform(RenderState::make_empty(), Transform::make_identity());
+  // want gsg to pass all state settings down so any non-matching defaults
+  // we set here get overwritten
 
   nassertv(_screen->_d3d9 != nullptr);
 
@@ -2803,7 +2802,7 @@ apply_fog(Fog *fog) {
  */
 void DXGraphicsStateGuardian9::
 do_issue_transform() {
-  const TransformState *transform = _internal_transform;
+  const Transform &transform = _internal_transform;
   DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1));
 
   if (_current_shader_context) {
@@ -2812,13 +2811,13 @@ do_issue_transform() {
 
 // ??? NO NEED TO SET THE D3D TRANSFORM VIA SetTransform SINCE THE TRANSFORM
 // IS ONLY USED IN THE SHADER
-    LMatrix4f mat = LCAST(float, transform->get_mat());
+    LMatrix4f mat = LCAST(float, transform.get_mat());
     const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data();
     _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
 
   }
   else {
-    LMatrix4f mat = LCAST(float, transform->get_mat());
+    LMatrix4f mat = LCAST(float, transform.get_mat());
     const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data();
     _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
 
@@ -3117,8 +3116,7 @@ do_issue_shade_model() {
  * _target.
  */
 void DXGraphicsStateGuardian9::
-set_state_and_transform(const RenderState *target,
-                        const TransformState *transform) {
+set_state_and_transform(const RenderState *target, const Transform &transform) {
 #ifndef NDEBUG
   if (gsg_cat.is_spam()) {
     gsg_cat.spam() << "Setting GSG state to " << (void *)target << ":\n";

+ 2 - 2
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -97,7 +97,7 @@ public:
   virtual void clear(DrawableRegion *clearable);
 
   virtual void prepare_display_region(DisplayRegionPipelineReader *dr);
-  virtual CPT(TransformState) calc_projection_mat(const Lens *lens);
+  virtual bool calc_projection_mat(LMatrix4 &result, const Lens *lens);
   virtual bool prepare_lens();
 
   virtual bool begin_frame(Thread *current_thread);
@@ -150,7 +150,7 @@ public:
   INLINE static DWORD LColor_to_D3DCOLOR(const LColor &cLColor);
 
   virtual void set_state_and_transform(const RenderState *state,
-                                       const TransformState *transform);
+                                       const Transform &transform);
 
   bool check_dx_allocation (HRESULT result, int allocation_size, int attempts);
 

+ 2 - 2
panda/src/egg2pg/characterMaker.cxx

@@ -24,7 +24,7 @@
 #include "characterSlider.h"
 #include "character.h"
 #include "geomNode.h"
-#include "transformState.h"
+#include "transform.h"
 #include "eggSurface.h"
 #include "eggCurve.h"
 #include "modelNode.h"
@@ -314,7 +314,7 @@ parent_joint_nodes(PartGroup *part) {
     if (joint_node != nullptr) {
       _character_node->add_child(joint_node);
       joint->add_net_transform(joint_node);
-      joint_node->set_transform(TransformState::make_mat(joint->_net_transform));
+      joint_node->set_transform(Transform::make_mat(joint->_net_transform));
     }
   }
 

+ 126 - 13
panda/src/egg2pg/eggLoader.cxx

@@ -20,7 +20,6 @@
 #include "config_egg.h"
 #include "nodePath.h"
 #include "renderState.h"
-#include "transformState.h"
 #include "texturePool.h"
 #include "billboardEffect.h"
 #include "decalEffect.h"
@@ -467,12 +466,126 @@ make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform,
   }
 }
 
+/**
+ * Creates a Transform object corresponding to the indicated EggTransform.
+ */
+Transform EggLoader::
+make_transform(const EggTransform *egg_transform) {
+  Transform ts = Transform::make_identity();
+  int num_components = egg_transform->get_num_components();
+  for (int i = 0; i < num_components; i++) {
+    switch (egg_transform->get_component_type(i)) {
+    case EggTransform::CT_translate2d:
+      {
+        LVecBase2 trans2d(LCAST(PN_stdfloat, egg_transform->get_component_vec2(i)));
+        LVecBase3 trans3d(trans2d[0], trans2d[1], 0.0f);
+        ts = Transform::make_pos(trans3d).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_translate3d:
+      {
+        LVecBase3 trans3d(LCAST(PN_stdfloat, egg_transform->get_component_vec3(i)));
+        ts = Transform::make_pos(trans3d).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_rotate2d:
+      {
+        LRotation rot(LVector3(0.0f, 0.0f, 1.0f),
+                       (PN_stdfloat)egg_transform->get_component_number(i));
+        ts = Transform::make_quat(rot).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_rotx:
+      {
+        LRotation rot(LVector3(1.0f, 0.0f, 0.0f),
+                       (PN_stdfloat)egg_transform->get_component_number(i));
+        ts = Transform::make_quat(rot).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_roty:
+      {
+        LRotation rot(LVector3(0.0f, 1.0f, 0.0f),
+                       (PN_stdfloat)egg_transform->get_component_number(i));
+        ts = Transform::make_quat(rot).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_rotz:
+      {
+        LRotation rot(LVector3(0.0f, 0.0f, 1.0f),
+                       (PN_stdfloat)egg_transform->get_component_number(i));
+        ts = Transform::make_quat(rot).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_rotate3d:
+      {
+        LRotation rot(LCAST(PN_stdfloat, egg_transform->get_component_vec3(i)),
+                       (PN_stdfloat)egg_transform->get_component_number(i));
+        ts = Transform::make_quat(rot).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_scale2d:
+      {
+        LVecBase2 scale2d(LCAST(PN_stdfloat, egg_transform->get_component_vec2(i)));
+        LVecBase3 scale3d(scale2d[0], scale2d[1], 1.0f);
+        ts = Transform::make_scale(scale3d).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_scale3d:
+      {
+        LVecBase3 scale3d(LCAST(PN_stdfloat, egg_transform->get_component_vec3(i)));
+        ts = Transform::make_scale(scale3d).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_uniform_scale:
+      {
+        PN_stdfloat scale = (PN_stdfloat)egg_transform->get_component_number(i);
+        ts = Transform::make_scale(scale).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_matrix3:
+      {
+        LMatrix3 m(LCAST(PN_stdfloat, egg_transform->get_component_mat3(i)));
+        LMatrix4 mat4(m(0, 0), m(0, 1), 0.0, m(0, 2),
+                       m(1, 0), m(1, 1), 0.0, m(1, 2),
+                       0.0, 0.0, 1.0, 0.0,
+                       m(2, 0), m(2, 1), 0.0, m(2, 2));
+
+        ts = Transform::make_mat(mat4).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_matrix4:
+      {
+        LMatrix4 mat4(LCAST(PN_stdfloat, egg_transform->get_component_mat4(i)));
+        ts = Transform::make_mat(mat4).compose(ts);
+      }
+      break;
+
+    case EggTransform::CT_invalid:
+      nassertr(false, ts);
+      break;
+    }
+  }
+
+  return ts;
+}
+
 /**
  * Creates a TransformState object corresponding to the indicated
  * EggTransform.
  */
 CPT(TransformState) EggLoader::
-make_transform(const EggTransform *egg_transform) {
+make_transform_state(const EggTransform *egg_transform) {
   // We'll build up the transform componentwise, so we preserve any
   // componentwise properties of the egg transform.
 
@@ -1925,7 +2038,7 @@ make_node(EggGroup *egg_group, PandaNode *parent) {
 
     // Piggy-back the desired transform to apply onto the node, since we can't
     // break the ABI in 1.10.
-    node->set_transform(TransformState::make_mat(LCAST(PN_stdfloat, egg_group->get_vertex_to_node())));
+    node->set_transform(Transform::make_mat(LCAST(PN_stdfloat, egg_group->get_vertex_to_node())));
     make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p());
     node->clear_transform();
 
@@ -2100,7 +2213,7 @@ create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
 
   // If the group had a transform, apply it to the node.
   if (egg_group->has_transform()) {
-    CPT(TransformState) transform = make_transform(egg_group);
+    Transform transform = make_transform(egg_group);
     node->set_transform(transform);
     node->set_prev_transform(transform);
   }
@@ -3026,7 +3139,7 @@ make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
           create_collision_plane(DCAST(EggPolygon, *ci), egg_group);
         if (csplane != nullptr) {
           apply_collision_flags(csplane, flags);
-          csplane->xform(cnode->get_transform()->get_mat());
+          csplane->xform(cnode->get_transform().get_mat());
           cnode->add_solid(csplane);
           return;
         }
@@ -3128,7 +3241,7 @@ make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
     CollisionSphere *cssphere =
       new CollisionSphere(center, radius);
     apply_collision_flags(cssphere, flags);
-    cssphere->xform(cnode->get_transform()->get_mat());
+    cssphere->xform(cnode->get_transform().get_mat());
     cnode->add_solid(cssphere);
   }
 }
@@ -3142,8 +3255,8 @@ make_collision_box(EggGroup *egg_group, CollisionNode *cnode,
                    EggGroup::CollideFlags flags) {
   LPoint3 min_p;
   LPoint3 max_p;
-  CPT(TransformState) transform = cnode->get_transform();
-  if (make_box(egg_group, flags, transform->get_mat(), min_p, max_p)) {
+  Transform transform = cnode->get_transform();
+  if (make_box(egg_group, flags, transform.get_mat(), min_p, max_p)) {
     CollisionBox *csbox =
       new CollisionBox(min_p, max_p);
     apply_collision_flags(csbox, flags);
@@ -3165,7 +3278,7 @@ make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode,
     CollisionInvSphere *cssphere =
       new CollisionInvSphere(center, radius);
     apply_collision_flags(cssphere, flags);
-    cssphere->xform(cnode->get_transform()->get_mat());
+    cssphere->xform(cnode->get_transform().get_mat());
     cnode->add_solid(cssphere);
   }
 }
@@ -3358,9 +3471,9 @@ make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode,
 
         CollisionCapsule *cscapsule =
           new CollisionCapsule(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b),
-                            radius);
+                               radius);
         apply_collision_flags(cscapsule, flags);
-        cscapsule->xform(cnode->get_transform()->get_mat());
+        cscapsule->xform(cnode->get_transform().get_mat());
         cnode->add_solid(cscapsule);
       }
     }
@@ -3522,7 +3635,7 @@ create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
         new CollisionPolygon(vertices_begin, vertices_end);
       if (cspoly->is_valid()) {
         apply_collision_flags(cspoly, flags);
-        cspoly->xform(cnode->get_transform()->get_mat());
+        cspoly->xform(cnode->get_transform().get_mat());
         cnode->add_solid(cspoly);
       }
     }
@@ -3613,7 +3726,7 @@ create_collision_floor_mesh(CollisionNode *cnode,
     CollisionFloorMesh::TriangleIndices triangle = *ti;
     csfloor->add_triangle(triangle.p1, triangle.p2, triangle.p3);
   }
-  csfloor->xform(cnode->get_transform()->get_mat());
+  csfloor->xform(cnode->get_transform().get_mat());
   cnode->add_solid(csfloor);
 }
 

+ 2 - 1
panda/src/egg2pg/eggLoader.h

@@ -77,7 +77,8 @@ public:
                     const LMatrix4d *transform, bool is_dynamic,
                     CharacterMaker *character_maker);
 
-  CPT(TransformState) make_transform(const EggTransform *egg_transform);
+  Transform make_transform(const EggTransform *egg_transform);
+  CPT(TransformState) make_transform_state(const EggTransform *egg_transform);
 
 private:
   class TextureDef {

+ 1 - 1
panda/src/egg2pg/eggRenderState.cxx

@@ -577,7 +577,7 @@ CPT(RenderAttrib) EggRenderState::
 apply_tex_mat(CPT(RenderAttrib) tex_mat_attrib,
               TextureStage *stage, const EggTexture *egg_tex) {
   if (egg_tex->has_transform()) {
-    CPT(TransformState) transform = _loader.make_transform(egg_tex);
+    CPT(TransformState) transform = _loader.make_transform_state(egg_tex);
 
     if (tex_mat_attrib == nullptr) {
       tex_mat_attrib = TexMatrixAttrib::make();

+ 18 - 8
panda/src/egg2pg/eggSaver.cxx

@@ -472,8 +472,8 @@ convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path,
   egg_group->set_collide_flags(EggGroup::CF_descend);
 
   NodePath np = node_path.get_node_path();
-  CPT(TransformState) net_transform = np.get_net_transform();
-  LMatrix4 net_mat = net_transform->get_mat();
+  Transform net_transform = np.get_net_transform();
+  LMatrix4 net_mat = net_transform.get_mat();
   LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv());
   net_mat = net_mat * inv;
 
@@ -696,8 +696,8 @@ convert_geom_node(GeomNode *node, const WorkingNodePath &node_path,
 
   NodePath np = node_path.get_node_path();
   CPT(RenderState) net_state = np.get_net_state();
-  CPT(TransformState) net_transform = np.get_net_transform();
-  LMatrix4 net_mat = net_transform->get_mat();
+  Transform net_transform = np.get_net_transform();
+  LMatrix4 net_mat = net_transform.get_mat();
   LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv());
   net_mat = net_mat * inv;
 
@@ -1103,9 +1103,9 @@ apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage
     }
   }
 
-  const TransformState *transform = node->get_transform();
-  if (!transform->is_identity()) {
-    if (transform->has_components()) {
+  Transform transform;
+  if (node->get_transform(transform)) {
+    /*if (transform->has_components()) {
       // If the transform can be represented componentwise, we prefer storing
       // it that way in the egg file.
       const LVecBase3 &scale = transform->get_scale();
@@ -1123,8 +1123,18 @@ apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage
 
     } else if (transform->has_mat()) {
       // Otherwise, we store the raw matrix.
-      const LMatrix4 &mat = transform->get_mat();
+      const LMatrix4 &mat = transform.get_mat();
       egg_group->set_transform3d(LCAST(double, mat));
+    }*/
+    LMatrix3 mat3 = transform.get_mat3();
+    if (mat3.almost_equal(LMatrix3::ident_mat())) {
+      LPoint3 pos = transform.get_pos();
+      if (!pos.almost_equal(LVecBase3::zero())) {
+        egg_group->add_translate3d(LCAST(double, pos));
+      }
+    } else {
+      // Otherwise, we store the raw matrix.
+      egg_group->set_transform3d(LCAST(double, transform.get_mat()));
     }
     any_applied = true;
   }

+ 2 - 2
panda/src/framework/windowFramework.cxx

@@ -506,9 +506,9 @@ center_trackball(const NodePath &object) {
   CPT(GeometricBoundingVolume) gbv = DCAST(GeometricBoundingVolume, volume);
 
   if (object.has_parent()) {
-    CPT(TransformState) net_transform = object.get_parent().get_net_transform();
+    Transform net_transform = object.get_parent().get_net_transform();
     PT(GeometricBoundingVolume) new_gbv = DCAST(GeometricBoundingVolume, gbv->make_copy());
-    new_gbv->xform(net_transform->get_mat());
+    new_gbv->xform(net_transform.get_mat());
     gbv = new_gbv;
   }
 

+ 5 - 5
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -424,9 +424,9 @@ unbind() {
  */
 void CLP(CgShaderContext)::
 set_state_and_transform(const RenderState *target_rs,
-                        const TransformState *modelview_transform,
-                        const TransformState *camera_transform,
-                        const TransformState *projection_transform) {
+                        const Transform &modelview_transform,
+                        const Transform &camera_transform,
+                        const LMatrix4 &projection_mat) {
 
   if (!valid()) {
     return;
@@ -443,8 +443,8 @@ set_state_and_transform(const RenderState *target_rs,
     _camera_transform = camera_transform;
     altered |= Shader::SSD_transform;
   }
-  if (_projection_transform != projection_transform) {
-    _projection_transform = projection_transform;
+  if (_projection_mat != projection_mat) {
+    _projection_mat = projection_mat;
     altered |= Shader::SSD_projection;
   }
 

+ 6 - 6
panda/src/glstuff/glCgShaderContext_src.h

@@ -38,9 +38,9 @@ public:
   void unbind() override;
 
   void set_state_and_transform(const RenderState *state,
-                               const TransformState *modelview_transform,
-                               const TransformState *camera_transform,
-                               const TransformState *projection_transform) override;
+                               const Transform &modelview_transform,
+                               const Transform &camera_transform,
+                               const LMatrix4 &projection_transform) override;
 
   void issue_parameters(int altered) override;
   void update_transform_table(const TransformTable *table);
@@ -80,9 +80,9 @@ private:
   pvector<CGparameter> _cg_parameter_map;
 
   WCPT(RenderState) _state_rs;
-  CPT(TransformState) _modelview_transform;
-  CPT(TransformState) _camera_transform;
-  CPT(TransformState) _projection_transform;
+  Transform _modelview_transform;
+  Transform _camera_transform;
+  LMatrix4 _projection_mat;
   GLint _frame_number;
 
   CLP(GraphicsStateGuardian) *_glgsg;

+ 40 - 43
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -4184,17 +4184,16 @@ clear_before_callback() {
  * this gsg.  Note that the projection matrix depends a lot upon the
  * coordinate system of the rendering API.
  *
- * The return value is a TransformState if the lens is acceptable, NULL if it
- * is not.
+ * The return value is true if the lens is acceptable, false if it is not.
  */
-CPT(TransformState) CLP(GraphicsStateGuardian)::
-calc_projection_mat(const Lens *lens) {
+bool CLP(GraphicsStateGuardian)::
+calc_projection_mat(LMatrix4 &result, const Lens *lens) {
   if (lens == nullptr) {
-    return nullptr;
+    return false;
   }
 
   if (!lens->is_linear()) {
-    return nullptr;
+    return false;
   }
 
   // The projection matrix must always be right-handed Y-up, even if our
@@ -4204,7 +4203,7 @@ calc_projection_mat(const Lens *lens) {
   // coordinate system, we'll use a Y-up projection matrix, and store the
   // conversion to our coordinate system of choice in the modelview matrix.
 
-  LMatrix4 result =
+  result =
     LMatrix4::convert_mat(_internal_coordinate_system,
                           lens->get_coordinate_system()) *
     lens->get_projection_mat(_current_stereo_channel);
@@ -4228,7 +4227,7 @@ calc_projection_mat(const Lens *lens) {
     result *= LMatrix4::scale_mat(1.0f, -1.0f, 1.0f);
   }
 
-  return TransformState::make_mat(result);
+  return true;
 }
 
 /**
@@ -4245,11 +4244,11 @@ prepare_lens() {
   if (has_fixed_function_pipeline()) {
     if (GLCAT.is_spam()) {
       GLCAT.spam()
-        << "glMatrixMode(GL_PROJECTION): " << _projection_mat->get_mat() << endl;
+        << "glMatrixMode(GL_PROJECTION): " << _projection_mat << endl;
     }
 
     glMatrixMode(GL_PROJECTION);
-    call_glLoadMatrix(_projection_mat->get_mat());
+    call_glLoadMatrix(_projection_mat);
     report_my_gl_errors();
 
     do_point_size();
@@ -6206,7 +6205,7 @@ end_draw_primitives() {
 #ifdef SUPPORT_FIXED_FUNCTION
   if (has_fixed_function_pipeline() && _transform_stale) {
     glMatrixMode(GL_MODELVIEW);
-    call_glLoadMatrix(_internal_transform->get_mat());
+    call_glLoadMatrix(_internal_transform.get_mat());
   }
 
   if (has_fixed_function_pipeline() && _data_reader->is_vertex_transformed()) {
@@ -8374,15 +8373,15 @@ do_issue_transform() {
   if (has_fixed_function_pipeline()) {
     // OpenGL ES 2 does not support glLoadMatrix.
 
-    const TransformState *transform = _internal_transform;
+    const Transform &transform = _internal_transform;
     if (GLCAT.is_spam()) {
       GLCAT.spam()
-        << "glLoadMatrix(GL_MODELVIEW): " << transform->get_mat() << endl;
+        << "glLoadMatrix(GL_MODELVIEW): " << transform.get_mat() << endl;
     }
 
     DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1));
     glMatrixMode(GL_MODELVIEW);
-    call_glLoadMatrix(transform->get_mat());
+    call_glLoadMatrix(transform.get_mat());
   }
 #endif
   _transform_stale = false;
@@ -9198,8 +9197,8 @@ bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
 
   // Position needs to specify x, y, z, and w w == 1 implies non-infinite
   // position
-  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
-  LPoint3 pos = light_obj->get_point() * transform->get_mat();
+  Transform transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
+  LPoint3 pos = transform.xform_point(light_obj->get_point());
 
   LPoint4 fpos(pos[0], pos[1], pos[2], 1.0f);
   call_glLightfv(id, GL_POSITION, fpos);
@@ -9240,8 +9239,8 @@ bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
   DirectionalLightFrameData &fdata = (*lookup.first).second;
   if (lookup.second) {
     // The light was not computed yet this frame.  Compute it now.
-    CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
-    LVector3 dir = light_obj->get_direction() * transform->get_mat();
+    Transform transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
+    LVector3 dir = transform.xform_vec(light_obj->get_direction());
     fdata._neg_dir.set(-dir[0], -dir[1], -dir[2], 0);
   }
 
@@ -9300,10 +9299,9 @@ bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
 
   // Position needs to specify x, y, z, and w w == 1 implies non-infinite
   // position
-  CPT(TransformState) transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
-  const LMatrix4 &light_mat = transform->get_mat();
-  LPoint3 pos = lens->get_nodal_point() * light_mat;
-  LVector3 dir = lens->get_view_vector() * light_mat;
+  Transform transform = light.get_transform(_scene_setup->get_scene_root().get_parent());
+  LPoint3 pos = transform.xform_point(lens->get_nodal_point());
+  LVector3 dir = transform.xform_vec(lens->get_view_vector());
 
   LPoint4 fpos(pos[0], pos[1], pos[2], 1.0f);
   call_glLightfv(id, GL_POSITION, fpos);
@@ -12186,12 +12184,12 @@ begin_bind_lights() {
   // to the root, by composing with the matrix computed by
   // _internal_transform->invert_compose(render_transform). But I think
   // loading a completely new matrix is simpler.)
-  CPT(TransformState) render_transform =
-    _cs_transform->compose(_scene_setup->get_world_transform());
+  Transform render_transform =
+    _cs_transform.compose(_scene_setup->get_world_transform());
 
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
-  call_glLoadMatrix(render_transform->get_mat());
+  call_glLoadMatrix(render_transform.get_mat());
 }
 #endif  // SUPPORT_FIXED_FUNCTION
 
@@ -12253,12 +12251,12 @@ begin_bind_clip_planes() {
   // relative to the root, by composing with the matrix computed by
   // _internal_transform->invert_compose(render_transform). But I think
   // loading a completely new matrix is simpler.)
-  CPT(TransformState) render_transform =
-    _cs_transform->compose(_scene_setup->get_world_transform());
+  Transform render_transform =
+    _cs_transform.compose(_scene_setup->get_world_transform());
 
   glMatrixMode(GL_MODELVIEW);
   glPushMatrix();
-  call_glLoadMatrix(render_transform->get_mat());
+  call_glLoadMatrix(render_transform.get_mat());
 }
 #endif  // SUPPORT_FIXED_FUNCTION
 
@@ -12274,10 +12272,10 @@ bind_clip_plane(const NodePath &plane, int plane_id) {
 
   GLenum id = get_clip_plane_id(plane_id);
 
-  CPT(TransformState) transform = plane.get_transform(_scene_setup->get_scene_root().get_parent());
+  Transform transform = plane.get_transform(_scene_setup->get_scene_root().get_parent());
   const PlaneNode *plane_node;
   DCAST_INTO_V(plane_node, plane.node());
-  LPlane xformed_plane = plane_node->get_plane() * transform->get_mat();
+  LPlane xformed_plane = plane_node->get_plane() * transform.get_mat();
 
 #ifdef OPENGLES
   // OpenGL ES uses a single-precision call.
@@ -12322,8 +12320,7 @@ end_bind_clip_planes() {
  * _target.
  */
 void CLP(GraphicsStateGuardian)::
-set_state_and_transform(const RenderState *target,
-                        const TransformState *transform) {
+set_state_and_transform(const RenderState *target, const Transform &transform) {
   report_my_gl_errors();
 #ifndef NDEBUG
   if (gsg_cat.is_spam()) {
@@ -13352,7 +13349,7 @@ do_issue_tex_gen() {
         // We need to rotate the normals out of GL's coordinate system and
         // into the user's coordinate system.  We do this by composing a
         // transform onto the texture matrix.
-        LMatrix4 mat = _inv_cs_transform->get_mat();
+        LMatrix4 mat = _inv_cs_transform.get_mat();
         mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
         glMatrixMode(GL_TEXTURE);
         GLPf(MultMatrix)(mat.get_data());
@@ -13377,9 +13374,9 @@ do_issue_tex_gen() {
         // matrix.  Unlike M_world_position, we can't achieve this effect by
         // monkeying with the modelview transform, since the current modelview
         // doesn't affect GL_REFLECTION_MAP.
-        CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
+        Transform camera_transform = _scene_setup->get_camera_transform().compose(_inv_cs_transform);
 
-        LMatrix4 mat = camera_transform->get_mat();
+        LMatrix4 mat = camera_transform.get_mat();
         mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
         glMatrixMode(GL_TEXTURE);
         GLPf(MultMatrix)(mat.get_data());
@@ -13402,7 +13399,7 @@ do_issue_tex_gen() {
         // We need to rotate the normals out of GL's coordinate system and
         // into the user's coordinate system.  We do this by composing a
         // transform onto the texture matrix.
-        LMatrix4 mat = _inv_cs_transform->get_mat();
+        LMatrix4 mat = _inv_cs_transform.get_mat();
         mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
         glMatrixMode(GL_TEXTURE);
         GLPf(MultMatrix)(mat.get_data());
@@ -13427,9 +13424,9 @@ do_issue_tex_gen() {
         // matrix.  Unlike M_world_position, we can't achieve this effect by
         // monkeying with the modelview transform, since the current modelview
         // doesn't affect GL_NORMAL_MAP.
-        CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
+        Transform camera_transform = _scene_setup->get_camera_transform().compose(_inv_cs_transform);
 
-        LMatrix4 mat = camera_transform->get_mat();
+        LMatrix4 mat = camera_transform.get_mat();
         mat.set_row(3, LVecBase3(0.0f, 0.0f, 0.0f));
         glMatrixMode(GL_TEXTURE);
         GLPf(MultMatrix)(mat.get_data());
@@ -13452,7 +13449,7 @@ do_issue_tex_gen() {
       // coordinate-system transform.
       glMatrixMode(GL_MODELVIEW);
       glPushMatrix();
-      call_glLoadMatrix(_cs_transform->get_mat());
+      call_glLoadMatrix(_cs_transform.get_mat());
 
       glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
       glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
@@ -13480,8 +13477,8 @@ do_issue_tex_gen() {
       {
         glMatrixMode(GL_MODELVIEW);
         glPushMatrix();
-        CPT(TransformState) root_transform = _cs_transform->compose(_scene_setup->get_world_transform());
-        call_glLoadMatrix(root_transform->get_mat());
+        Transform root_transform = _cs_transform.compose(_scene_setup->get_world_transform());
+        call_glLoadMatrix(root_transform.get_mat());
         glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
         glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
         glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
@@ -15871,8 +15868,8 @@ do_point_size() {
     // To arrange that, we need to figure out the appropriate scaling factor
     // based on the current viewport and projection matrix.
     LVector3 height(0.0f, _point_size, 1.0f);
-    height = height * _projection_mat->get_mat();
-    height = height * _internal_transform->get_scale()[1];
+    height = height * _projection_mat;
+    height = height * _internal_transform.get_scale()[1];
     PN_stdfloat s = height[1] * _viewport_height / _point_size;
 
     if (_current_lens->is_orthographic()) {

+ 2 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -293,7 +293,7 @@ public:
 
   virtual void prepare_display_region(DisplayRegionPipelineReader *dr);
   virtual void clear_before_callback();
-  virtual CPT(TransformState) calc_projection_mat(const Lens *lens);
+  virtual bool calc_projection_mat(LMatrix4 &result, const Lens *lens);
   virtual bool prepare_lens();
 
   virtual bool begin_frame(Thread *current_thread);
@@ -455,7 +455,7 @@ public:
   INLINE int get_max_vertex_attrib_stride() const;
 
   virtual void set_state_and_transform(const RenderState *state,
-                                       const TransformState *transform);
+                                       const Transform &transform);
 
   void bind_fbo(GLuint fbo);
   virtual bool get_supports_cg_profile(const std::string &name) const;

+ 5 - 5
panda/src/glstuff/glShaderContext_src.cxx

@@ -2227,9 +2227,9 @@ unbind() {
  */
 void CLP(ShaderContext)::
 set_state_and_transform(const RenderState *target_rs,
-                        const TransformState *modelview_transform,
-                        const TransformState *camera_transform,
-                        const TransformState *projection_transform) {
+                        const Transform &modelview_transform,
+                        const Transform &camera_transform,
+                        const LMatrix4 &projection_mat) {
 
   // Find out which state properties have changed.
   int altered = 0;
@@ -2242,8 +2242,8 @@ set_state_and_transform(const RenderState *target_rs,
     _camera_transform = camera_transform;
     altered |= Shader::SSD_transform;
   }
-  if (_projection_transform != projection_transform) {
-    _projection_transform = projection_transform;
+  if (_projection_mat != projection_mat) {
+    _projection_mat = projection_mat;
     altered |= Shader::SSD_projection;
   }
 

+ 6 - 6
panda/src/glstuff/glShaderContext_src.h

@@ -46,9 +46,9 @@ public:
   void unbind() override;
 
   void set_state_and_transform(const RenderState *state,
-                               const TransformState *modelview_transform,
-                               const TransformState *camera_transform,
-                               const TransformState *projection_transform) override;
+                               const Transform &modelview_transform,
+                               const Transform &camera_transform,
+                               const LMatrix4 &projection_mat) override;
 
   void issue_parameters(int altered) override;
   void update_transform_table(const TransformTable *table);
@@ -73,9 +73,9 @@ private:
   GLSLShaders _glsl_shaders;
 
   WCPT(RenderState) _state_rs;
-  CPT(TransformState) _modelview_transform;
-  CPT(TransformState) _camera_transform;
-  CPT(TransformState) _projection_transform;
+  Transform _modelview_transform;
+  Transform _camera_transform;
+  LMatrix4 _projection_mat;
   CPT(ColorAttrib) _color_attrib;
   WCPT(ShaderAttrib) _shader_attrib;
 

+ 6 - 3
panda/src/gobj/shaderContext.h

@@ -18,6 +18,9 @@
 #include "internalName.h"
 #include "savedContext.h"
 #include "shader.h"
+#include "lmatrix.h"
+
+class Transform;
 
 /**
  * The ShaderContext is meant to contain the compiled version of a shader
@@ -33,9 +36,9 @@ public:
   INLINE ShaderContext(Shader *se);
 
   virtual void set_state_and_transform(const RenderState *,
-                                       const TransformState *,
-                                       const TransformState *,
-                                       const TransformState *) {};
+                                       const Transform &,
+                                       const Transform &,
+                                       const LMatrix4 &) {};
 
   INLINE virtual bool valid() { return false; }
   INLINE virtual void bind() {};

+ 2 - 2
panda/src/grutil/cardMaker.cxx

@@ -255,8 +255,8 @@ rescale_source_geometry() {
 
   LVector3 trans = frame_ctr - geom_center;
 
-  CPT(TransformState) transform =
-    TransformState::make_pos_hpr_scale(trans, LPoint3(0.0f, 0.0f, 0.0f), scale);
+  Transform transform =
+    Transform::make_pos_hpr_scale(trans, LPoint3(0.0f, 0.0f, 0.0f), scale);
   root->set_transform(transform);
 
   if (_has_color) {

+ 2 - 2
panda/src/grutil/frameRateMeter.cxx

@@ -182,10 +182,10 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 
   // Scale the transform by the calculated aspect ratio.
   if (aspect_ratio != _last_aspect_ratio) {
-    _aspect_ratio_transform = TransformState::make_scale(LVecBase3(aspect_ratio, 1, 1));
+    _aspect_ratio_transform = Transform::make_scale(LVecBase3(aspect_ratio, 1, 1));
     _last_aspect_ratio = aspect_ratio;
   }
-  data._net_transform = data._net_transform->compose(_aspect_ratio_transform);
+  data._net_transform = data._net_transform.compose(_aspect_ratio_transform);
 
   // Check to see if it's time to update.
   double now = _clock_object->get_frame_time(current_thread);

+ 1 - 1
panda/src/grutil/frameRateMeter.h

@@ -74,7 +74,7 @@ private:
   ClockObject *_clock_object;
 
   PN_stdfloat _last_aspect_ratio;
-  CPT(TransformState) _aspect_ratio_transform;
+  Transform _aspect_ratio_transform;
 
   static PStatCollector _show_fps_pcollector;
 

+ 1 - 1
panda/src/grutil/multitexReducer.I

@@ -23,7 +23,7 @@
  */
 INLINE void MultitexReducer::
 scan(const NodePath &node) {
-  scan(node.node(), RenderState::make_empty(), TransformState::make_identity());
+  scan(node.node(), RenderState::make_empty(), Transform::make_identity());
 }
 
 /**

+ 5 - 5
panda/src/grutil/multitexReducer.cxx

@@ -77,14 +77,14 @@ clear() {
  * indicated starting state.
  */
 void MultitexReducer::
-scan(PandaNode *node, const RenderState *state, const TransformState *transform) {
+scan(PandaNode *node, const RenderState *state, const Transform &transform) {
   if (grutil_cat.is_debug()) {
     grutil_cat.debug()
-      << "scan(" << *node << ", " << *state << ", " << *transform << ")\n";
+      << "scan(" << *node << ", " << *state << ", " << transform << ")\n";
   }
 
   CPT(RenderState) next_state = state->compose(node->get_state());
-  CPT(TransformState) next_transform = transform->compose(node->get_transform());
+  Transform next_transform = transform.compose(node->get_transform());
 
   // We must turn off any textures we come across in the scan() operation,
   // since the flattened texture will be applied to the Geoms after the
@@ -423,11 +423,11 @@ flatten(GraphicsOutput *window) {
  */
 void MultitexReducer::
 scan_geom_node(GeomNode *node, const RenderState *state,
-               const TransformState *transform) {
+               const Transform &transform) {
   if (grutil_cat.is_debug()) {
     grutil_cat.debug()
       << "scan_geom_node(" << *node << ", " << *state << ", "
-      << *transform << ")\n";
+      << transform << ")\n";
   }
 
   _geom_node_list.push_back(GeomNodeInfo(state, node));

+ 3 - 5
panda/src/grutil/multitexReducer.h

@@ -19,7 +19,7 @@
 #include "textureAttrib.h"
 #include "textureStage.h"
 #include "texMatrixAttrib.h"
-#include "transformState.h"
+#include "transform.h"
 #include "geomNode.h"
 #include "nodePath.h"
 #include "luse.h"
@@ -30,7 +30,6 @@
 class GraphicsOutput;
 class PandaNode;
 class RenderState;
-class TransformState;
 
 /**
  * This object presents an interface for generating new texture images that
@@ -52,8 +51,7 @@ PUBLISHED:
   void clear();
   INLINE void scan(const NodePath &node);
   INLINE void scan(const NodePath &node, const NodePath &state_from);
-  void scan(PandaNode *node, const RenderState *state,
-            const TransformState *transform);
+  void scan(PandaNode *node, const RenderState *state, const Transform &transform);
 
   void set_target(TextureStage *stage);
   void set_use_geom(bool use_geom);
@@ -108,7 +106,7 @@ private:
 
 private:
   void scan_geom_node(GeomNode *node, const RenderState *state,
-                      const TransformState *transform);
+                      const Transform &transform);
 
   void record_stage_list(const StageList &stage_list,
                          const GeomInfo &geom_info);

+ 2 - 2
panda/src/grutil/nodeVertexTransform.cxx

@@ -35,10 +35,10 @@ get_matrix(LMatrix4 &matrix) const {
   if (_prev != nullptr) {
     LMatrix4 prev_matrix;
     _prev->get_matrix(prev_matrix);
-    matrix.multiply(_node->get_transform()->get_mat(), prev_matrix);
+    matrix.multiply(_node->get_transform().get_mat(), prev_matrix);
 
   } else {
-    matrix = _node->get_transform()->get_mat();
+    matrix = _node->get_transform().get_mat();
   }
 }
 

+ 35 - 37
panda/src/grutil/pipeOcclusionCullTraverser.cxx

@@ -158,17 +158,17 @@ set_scene(SceneSetup *scene_setup, GraphicsStateGuardianBase *gsgbase,
     // not from the camera root.
     NodePath cull_center = _scene->get_cull_center();
     NodePath scene_parent = _scene->get_scene_root().get_parent(current_thread);
-    CPT(TransformState) camera_transform = cull_center.get_transform(scene_parent, current_thread);
-    CPT(TransformState) world_transform = scene_parent.get_transform(cull_center, current_thread);
-    CPT(TransformState) cs_world_transform = _scene->get_cs_transform()->compose(world_transform);
+    Transform camera_transform = cull_center.get_transform(scene_parent, current_thread);
+    Transform world_transform = scene_parent.get_transform(cull_center, current_thread);
+    Transform cs_world_transform = _scene->get_cs_transform().compose(world_transform);
     _scene->set_camera_transform(camera_transform);
     _scene->set_world_transform(world_transform);
     _scene->set_cs_world_transform(cs_world_transform);
 
     // We use this to recover the original net_transform.
-    _inv_cs_world_transform = cs_world_transform->get_inverse();
+    _inv_cs_world_transform = cs_world_transform.get_inverse();
   } else {
-    _inv_cs_world_transform = _scene->get_cs_world_transform()->get_inverse();
+    _inv_cs_world_transform = _scene->get_cs_world_transform().get_inverse();
   }
 
   nassertv(_scene->get_cs_transform() == scene_setup->get_cs_transform());
@@ -319,8 +319,8 @@ is_in_view(CullTraverserData &data) {
     // occlusion test on this particular node.  Do it.
 
     CPT(BoundingVolume) vol = node_reader->get_bounds();
-    CPT(TransformState) net_transform = data.get_net_transform(this);
-    CPT(TransformState) internal_transform;
+    Transform net_transform = data.get_net_transform(this);
+    Transform internal_transform;
 
     CPT(Geom) geom;
     if (get_volume_viz(vol, geom, net_transform, internal_transform)) {
@@ -385,8 +385,8 @@ record_object(CullableObject *object, const CullTraverser *traverser) {
   } else {
     // Issue an occlusion test for this object.
     CPT(BoundingVolume) vol = object->_geom->get_bounds(current_thread);
-    CPT(TransformState) net_transform = _inv_cs_world_transform->compose(object->_internal_transform);
-    CPT(TransformState) internal_transform;
+    Transform net_transform = _inv_cs_world_transform.compose(object->_internal_transform);
+    Transform internal_transform;
     CPT(Geom) geom;
     if (get_volume_viz(vol, geom, net_transform, internal_transform)) {
       pobj._query =
@@ -512,8 +512,8 @@ make_solid_test_state() {
 bool PipeOcclusionCullTraverser::
 get_volume_viz(const BoundingVolume *vol,
                CPT(Geom) &geom,  // OUT
-               CPT(TransformState) &net_transform, // IN-OUT
-               CPT(TransformState) &internal_transform  // OUT
+               Transform &net_transform, // IN-OUT
+               Transform &internal_transform  // OUT
                ) {
   if (vol->is_infinite() || vol->is_empty()) {
     return false;
@@ -521,28 +521,28 @@ get_volume_viz(const BoundingVolume *vol,
 
   if (vol->is_exact_type(BoundingSphere::get_class_type())) {
     const BoundingSphere *sphere = DCAST(BoundingSphere, vol);
-    CPT(TransformState) local_transform =
-      TransformState::make_pos_hpr_scale(sphere->get_center(),
-                                         LVecBase3(0, 0, 0),
-                                         sphere->get_radius());
-    net_transform = net_transform->compose(local_transform);
+    Transform local_transform =
+      Transform::make_pos_hpr_scale(sphere->get_center(),
+                                    LVecBase3(0, 0, 0),
+                                    sphere->get_radius());
+    net_transform = net_transform.compose(local_transform);
 
-    CPT(TransformState) modelview_transform =
-      _internal_trav->get_world_transform()->compose(net_transform);
+    Transform modelview_transform =
+      _internal_trav->get_world_transform().compose(net_transform);
 
     // See if the bounding sphere is clipped by the near plane.  If it is, the
     // occlusion test may fail, so we won't bother performing it for this
     // object.  Anyway, it's not occluded by anything, since it's intersecting
     // the near plane.
-    const LPoint3 &center = modelview_transform->get_pos();
-    const LVecBase3 &radius = modelview_transform->get_scale();
+    LPoint3 center = modelview_transform.get_pos();
+    LVecBase3 radius = modelview_transform.get_scale();
     if (center[1] - radius[1] < 0.0f) {
       return false;
     }
 
     // Construct the internal transform for the internal traverser.
     internal_transform = _internal_trav->get_scene()->
-      get_cs_transform()->compose(modelview_transform);
+      get_cs_transform().compose(modelview_transform);
 
     // The sphere looks good.
     geom = _sphere_geom;
@@ -550,14 +550,14 @@ get_volume_viz(const BoundingVolume *vol,
 
   } else if (vol->is_exact_type(BoundingBox::get_class_type())) {
     const BoundingBox *box = DCAST(BoundingBox, vol);
-    CPT(TransformState) local_transform =
-      TransformState::make_pos_hpr_scale(box->get_minq(),
-                                         LVecBase3(0, 0, 0),
-                                         box->get_maxq() - box->get_minq());
-    net_transform = net_transform->compose(local_transform);
+    Transform local_transform =
+      Transform::make_pos_hpr_scale(box->get_minq(),
+                                    LVecBase3(0, 0, 0),
+                                    box->get_maxq() - box->get_minq());
+    net_transform = net_transform.compose(local_transform);
 
-    CPT(TransformState) modelview_transform =
-      _internal_trav->get_world_transform()->compose(net_transform);
+    Transform modelview_transform =
+      _internal_trav->get_world_transform().compose(net_transform);
 
     // See if the bounding box is clipped by the near plane.  If it is, the
     // occlusion test may fail, so we won't bother performing it for this
@@ -573,9 +573,8 @@ get_volume_viz(const BoundingVolume *vol,
       LPoint3(1.0f, 1.0f, 0.0f),
       LPoint3(1.0f, 1.0f, 1.0f),
     };
-    const LMatrix4 &mat = modelview_transform->get_mat();
     for (int i = 0; i < 8; ++i) {
-      LPoint3 p = points[i] * mat;
+      LPoint3 p = modelview_transform.xform_point(points[i]);
       if (p[1] < 0.0f) {
         return false;
       }
@@ -583,7 +582,7 @@ get_volume_viz(const BoundingVolume *vol,
 
     // Construct the internal transform for the internal traverser.
     internal_transform = _internal_trav->get_scene()->
-      get_cs_transform()->compose(modelview_transform);
+      get_cs_transform().compose(modelview_transform);
 
     // The box looks good.
     geom = _box_geom;
@@ -599,8 +598,8 @@ get_volume_viz(const BoundingVolume *vol,
  * visibility.
  */
 PT(OcclusionQueryContext) PipeOcclusionCullTraverser::
-perform_occlusion_test(const Geom *geom, const TransformState *net_transform,
-                       const TransformState *internal_transform) {
+perform_occlusion_test(const Geom *geom, const Transform &net_transform,
+                       const Transform &internal_transform) {
   _occlusion_tests_pcollector.add_level(1);
   PStatTimer timer(_test_occlusion_pcollector);
 
@@ -637,8 +636,8 @@ perform_occlusion_test(const Geom *geom, const TransformState *net_transform,
  */
 void PipeOcclusionCullTraverser::
 show_results(int num_fragments, const Geom *geom,
-             const TransformState *net_transform,
-             const TransformState *internal_transform) {
+             const Transform &net_transform,
+             const Transform &internal_transform) {
   LColor color;
   if (num_fragments == 0) {
     // Magenta: culled
@@ -659,8 +658,7 @@ show_results(int num_fragments, const Geom *geom,
   _internal_cull_handler->record_object(internal_viz, _internal_trav);
 
   // Also render the viz in the main scene.
-  internal_transform = get_scene()->get_cs_world_transform()->compose(net_transform);
   CullableObject *main_viz =
-    new CullableObject(geom, state, internal_transform);
+    new CullableObject(geom, state, get_scene()->get_cs_world_transform().compose(net_transform));
   _true_cull_handler->record_object(main_viz, this);
 }

+ 7 - 7
panda/src/grutil/pipeOcclusionCullTraverser.h

@@ -71,17 +71,17 @@ private:
 
   bool get_volume_viz(const BoundingVolume *vol,
                       CPT(Geom) &geom,  // OUT
-                      CPT(TransformState) &net_transform, // IN-OUT
-                      CPT(TransformState) &internal_transform  // OUT
+                      Transform &net_transform, // IN-OUT
+                      Transform &internal_transform  // OUT
                       );
   PT(OcclusionQueryContext)
     perform_occlusion_test(const Geom *geom,
-                           const TransformState *net_transform,
-                           const TransformState *internal_transform);
+                           const Transform &net_transform,
+                           const Transform &internal_transform);
 
   void show_results(int num_fragments, const Geom *geom,
-                    const TransformState *net_transform,
-                    const TransformState *internal_transform);
+                    const Transform &net_transform,
+                    const Transform &internal_transform);
 private:
   bool _live;
 
@@ -92,7 +92,7 @@ private:
 
   PT(SceneSetup) _scene;
   PT(CullTraverser) _internal_trav;
-  CPT(TransformState) _inv_cs_world_transform;
+  Transform _inv_cs_world_transform;
 
   CullHandler *_internal_cull_handler;
   CullHandler *_true_cull_handler;

+ 2 - 1
panda/src/grutil/rigidBodyCombiner.cxx

@@ -161,7 +161,8 @@ r_collect(PandaNode *node, const RenderState *state,
           const VertexTransform *transform) {
   CPT(RenderState) next_state = state->compose(node->get_state());
   CPT(VertexTransform) next_transform = transform;
-  if (!node->get_transform()->is_identity() ||
+  Transform node_transform;
+  if (node->get_transform(node_transform) ||
       (node->is_of_type(ModelNode::get_class_type()) &&
        DCAST(ModelNode, node)->get_preserve_transform() != ModelNode::PT_none)) {
     // This node has a transform we need to keep.

+ 2 - 2
panda/src/grutil/sceneGraphAnalyzerMeter.cxx

@@ -163,10 +163,10 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
 
   // Scale the transform by the calculated aspect ratio.
   if (aspect_ratio != _last_aspect_ratio) {
-    _aspect_ratio_transform = TransformState::make_scale(LVecBase3(aspect_ratio, 1, 1));
+    _aspect_ratio_transform = Transform::make_scale(LVecBase3(aspect_ratio, 1, 1));
     _last_aspect_ratio = aspect_ratio;
   }
-  data._net_transform = data._net_transform->compose(_aspect_ratio_transform);
+  data._net_transform = data._net_transform.compose(_aspect_ratio_transform);
 
   // Check to see if it's time to update.
   double now = _clock_object->get_frame_time(current_thread);

+ 1 - 1
panda/src/grutil/sceneGraphAnalyzerMeter.h

@@ -73,7 +73,7 @@ private:
   ClockObject *_clock_object;
 
   PN_stdfloat _last_aspect_ratio;
-  CPT(TransformState) _aspect_ratio_transform;
+  Transform _aspect_ratio_transform;
 
   static PStatCollector _show_analyzer_pcollector;
 

+ 10 - 11
panda/src/grutil/shaderTerrainMesh.cxx

@@ -478,7 +478,7 @@ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &dat
   }
 
   // Get transform and render state for this render pass
-  CPT(TransformState) modelview_transform = data.get_internal_transform(trav);
+  Transform modelview_transform = data.get_internal_transform(trav);
   CPT(RenderState) state = data._state->compose(get_state());
 
   // Store a handle to the scene setup
@@ -492,12 +492,12 @@ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &dat
   PT(BoundingVolume) cam_bounds = scene->get_cull_bounds();
 
   // Transform the camera bounds with the main camera transform
-  DCAST(GeometricBoundingVolume, cam_bounds)->xform(scene->get_camera_transform()->get_mat());
+  DCAST(GeometricBoundingVolume, cam_bounds)->xform(scene->get_camera_transform().get_mat());
 
   TraversalData traversal_data;
   traversal_data.cam_bounds = cam_bounds;
-  traversal_data.model_mat = get_transform()->get_mat();
-  traversal_data.mvp_mat = modelview_transform->get_mat() * projection_mat;
+  traversal_data.model_mat = get_transform().get_mat();
+  traversal_data.mvp_mat = modelview_transform.get_mat() * projection_mat;
   traversal_data.emitted_chunks = 0;
   traversal_data.storage_ptr = (ChunkDataEntry*)_data_texture->modify_ram_image().p();
   traversal_data.screen_size.set(scene->get_viewport_width(), scene->get_viewport_height());
@@ -568,16 +568,15 @@ void ShaderTerrainMesh::add_for_draw(CullTraverser *trav, CullTraverserData &dat
  * over several nodes, so it may enter with min_point, max_point, and
  * found_any already set.
  */
-CPT(TransformState) ShaderTerrainMesh::
+Transform ShaderTerrainMesh::
 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
-                  const TransformState *transform, Thread *current_thread) const {
-  CPT(TransformState) next_transform =
+                  const Transform &transform, Thread *current_thread) const {
+  Transform next_transform =
     PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform,
                                  current_thread);
 
-  const LMatrix4 &mat = next_transform->get_mat();
-  LPoint3 terrain_min_point = LPoint3(0, 0, 0) * mat;
-  LPoint3 terrain_max_point = LPoint3(1, 1, 1) * mat;
+  LPoint3 terrain_min_point = next_transform.xform_point(LPoint3(0, 0, 0));
+  LPoint3 terrain_max_point = next_transform.xform_point(LPoint3(1, 1, 1));
   if (!found_any) {
     min_point = terrain_min_point;
     max_point = terrain_max_point;
@@ -780,5 +779,5 @@ LPoint3 ShaderTerrainMesh::uv_to_world(const LTexCoord& coord) const {
     return LPoint3(0);
   }
   LPoint3 unit_point(coord.get_x(), coord.get_y(), result.get_x());
-  return get_transform()->get_mat().xform_point_general(unit_point);
+  return get_transform().xform_point(unit_point);
 }

+ 2 - 3
panda/src/grutil/shaderTerrainMesh.h

@@ -90,10 +90,9 @@ public:
   virtual void add_for_draw(CullTraverser *trav, CullTraverserData &data);
 
 private:
-  virtual CPT(TransformState)
+  virtual Transform
     calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
-                      bool &found_any,
-                      const TransformState *transform,
+                      bool &found_any, const Transform &transform,
                       Thread *current_thread = Thread::get_current_thread()) const;
 
   virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,

+ 2 - 2
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -63,7 +63,7 @@ class Shader;
 class ShaderContext;
 class ShaderBuffer;
 class RenderState;
-class TransformState;
+class Transform;
 class Material;
 
 class ColorScaleAttrib;
@@ -180,7 +180,7 @@ public:
                                          Thread *current_thread)=0;
 
   virtual void set_state_and_transform(const RenderState *state,
-                                       const TransformState *transform)=0;
+                                       const Transform &transform)=0;
 
   // This function may only be called during a render traversal; it will
   // compute the distance to the indicated point, assumed to be in eye

+ 4 - 6
panda/src/parametrics/nurbsSurfaceEvaluator.cxx

@@ -236,9 +236,8 @@ get_vertices(NurbsSurfaceEvaluator::Vert4Array &verts, const NodePath &rel_to) c
     if (space.is_empty()) {
       verts.push_back(vertex);
     } else {
-      CPT(TransformState) transform = space.get_transform(rel_to);
-      const LMatrix4 &mat = transform->get_mat();
-      verts.push_back(vertex * mat);
+      Transform transform = space.get_transform(rel_to);
+      verts.push_back(vertex * transform.get_mat());
     }
   }
 }
@@ -260,9 +259,8 @@ get_vertices(NurbsSurfaceEvaluator::Vert3Array &verts, const NodePath &rel_to) c
     const NodePath &space = _vertices[vi].get_space(rel_to);
     LVecBase4 vertex = _vertices[vi].get_vertex();
     if (!space.is_empty()) {
-      CPT(TransformState) transform = space.get_transform(rel_to);
-      const LMatrix4 &mat = transform->get_mat();
-      vertex = vertex * mat;
+      Transform transform = space.get_transform(rel_to);
+      vertex = vertex * transform.get_mat();
     }
     LPoint3 v3(vertex[0] / vertex[3], vertex[1] / vertex[3], vertex[2] / vertex[3]);
     verts.push_back(v3);

+ 5 - 5
panda/src/parametrics/ropeNode.cxx

@@ -391,12 +391,12 @@ render_tape(CullTraverser *trav, CullTraverserData &data,
 void RopeNode::
 render_billboard(CullTraverser *trav, CullTraverserData &data,
                  NurbsCurveResult *result) const {
-  const TransformState *net_transform = data.get_net_transform(trav);
-  const TransformState *camera_transform = trav->get_camera_transform();
+  Transform net_transform = data.get_net_transform(trav);
+  Transform camera_transform = trav->get_camera_transform();
 
-  CPT(TransformState) rel_transform =
-    net_transform->invert_compose(camera_transform);
-  LVector3 camera_vec = LVector3::forward() * rel_transform->get_mat();
+  Transform rel_transform =
+    net_transform.invert_compose(camera_transform);
+  LVector3 camera_vec = rel_transform.xform_vec(LVector3::forward());
 
   CurveSegments curve_segments;
   int num_curve_verts = get_connected_segments(curve_segments, result);

+ 2 - 2
panda/src/particlesystem/geomParticleRenderer.cxx

@@ -14,7 +14,7 @@
 #include "geomParticleRenderer.h"
 #include "baseParticle.h"
 
-#include "transformState.h"
+#include "transform.h"
 #include "colorScaleAttrib.h"
 #include "colorAttrib.h"
 #include "pStatTimer.h"
@@ -231,7 +231,7 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
         }
       }
 
-      cur_node->set_transform(TransformState::make_pos_quat_scale
+      cur_node->set_transform(Transform::make_pos_quat_scale
                               (cur_particle->get_position(),
                                cur_particle->get_orientation(),
                                LVecBase3(current_x_scale, current_y_scale, current_z_scale)));

+ 8 - 11
panda/src/particlesystem/particleSystem.cxx

@@ -175,15 +175,15 @@ birth_particle() {
   NodePath physical_np = get_physical_node_path();
   NodePath render_np = _renderer->get_render_node_path();
 
-  CPT(TransformState) transform = physical_np.get_transform(render_np);
-  const LMatrix4 &birth_to_render_xform = transform->get_mat();
-  world_pos = new_pos * birth_to_render_xform;
+  Transform transform = physical_np.get_transform(render_np);
+  world_pos = transform.xform_point(new_pos);
 
   // cout << "New particle at " << world_pos << endl;
 
   // possibly transform the initial velocity as well.
-  if (_local_velocity_flag == false)
-    new_vel = new_vel * birth_to_render_xform;
+  if (!_local_velocity_flag) {
+    new_vel = transform.xform_vec(new_vel);
+  }
 
   bp->reset_position(world_pos/* + (NORMALIZED_RAND() * new_vel)*/);
   bp->set_velocity(new_vel);
@@ -270,13 +270,10 @@ spawn_child_system(BaseParticle *bp) {
   // child.
   parent->add_child(new_pn);
 
-  CPT(TransformState) transform = physical_np.get_transform(parent_np);
-  const LMatrix4 &old_system_to_parent_xform = transform->get_mat();
-
-  LMatrix4 child_space_xform = old_system_to_parent_xform *
-    bp->get_lcs();
+  Transform transform = physical_np.get_transform(parent_np);
+  LMatrix4 child_space_xform = transform.get_mat() * bp->get_lcs();
 
-  new_pn->set_transform(TransformState::make_mat(child_space_xform));
+  new_pn->set_transform(Transform::make_mat(child_space_xform));
 
   // tack the new system onto the managers
   _manager->attach_particlesystem(new_ps);

+ 14 - 8
panda/src/pgraph/accumulatedAttribs.cxx

@@ -28,7 +28,7 @@
  */
 AccumulatedAttribs::
 AccumulatedAttribs() {
-  _transform = TransformState::make_identity();
+  _transform = Transform::make_identity();
   _color_override = 0;
   _color_scale_override = 0;
   _tex_matrix_override = 0;
@@ -44,6 +44,7 @@ AccumulatedAttribs() {
 AccumulatedAttribs::
 AccumulatedAttribs(const AccumulatedAttribs &copy) :
   _transform(copy._transform),
+  _transform_state(copy._transform_state),
   _color(copy._color),
   _color_override(copy._color_override),
   _color_scale(copy._color_scale),
@@ -66,6 +67,7 @@ AccumulatedAttribs(const AccumulatedAttribs &copy) :
 void AccumulatedAttribs::
 operator = (const AccumulatedAttribs &copy) {
   _transform = copy._transform;
+  _transform_state = copy._transform_state;
   _color = copy._color;
   _color_override = copy._color_override;
   _color_scale = copy._color_scale;
@@ -87,7 +89,7 @@ operator = (const AccumulatedAttribs &copy) {
 void AccumulatedAttribs::
 write(std::ostream &out, int attrib_types, int indent_level) const {
   if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
-    _transform->write(out, indent_level);
+    _transform.write(out, indent_level);
   }
   if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
     if (_color == nullptr) {
@@ -137,10 +139,13 @@ void AccumulatedAttribs::
 collect(PandaNode *node, int attrib_types) {
   if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
     // Collect the node's transform.
-    nassertv(_transform != nullptr);
-    _transform = _transform->compose(node->get_transform());
-    node->set_transform(TransformState::make_identity());
-    node->set_prev_transform(TransformState::make_identity());
+    Transform node_transform;
+    if (node->get_transform(node_transform)) {
+      _transform = _transform.compose(node_transform);
+      _transform_state.clear();
+      node->set_transform(Transform::make_identity());
+    }
+    node->set_prev_transform(Transform::make_identity());
   }
 
   CPT(RenderState) new_state = collect(node->get_state(), attrib_types);
@@ -285,9 +290,10 @@ collect(const RenderState *state, int attrib_types) {
 void AccumulatedAttribs::
 apply_to_node(PandaNode *node, int attrib_types) {
   if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
-    node->set_transform(_transform->compose(node->get_transform())->get_unique());
+    node->set_transform(_transform.compose(node->get_transform()));
     node->reset_prev_transform();
-    _transform = TransformState::make_identity();
+    _transform = Transform::make_identity();
+    _transform_state.clear();
   }
 
   if ((attrib_types & SceneGraphReducer::TT_color) != 0) {

+ 3 - 2
panda/src/pgraph/accumulatedAttribs.h

@@ -15,7 +15,7 @@
 #define ACCUMULATEDATTRIBS_H
 
 #include "pandabase.h"
-#include "transformState.h"
+#include "transform.h"
 #include "renderAttrib.h"
 #include "renderState.h"
 #include "pointerTo.h"
@@ -39,7 +39,8 @@ public:
   CPT(RenderState) collect(const RenderState *state, int attrib_types);
   void apply_to_node(PandaNode *node, int attrib_types);
 
-  CPT(TransformState) _transform;
+  Transform _transform;
+  mutable CPT(TransformState) _transform_state;
   CPT(RenderAttrib) _color;
   int _color_override;
   CPT(RenderAttrib) _color_scale;

+ 31 - 36
panda/src/pgraph/billboardEffect.cxx

@@ -56,11 +56,13 @@ safe_to_transform() const {
  * through) this node due to a flatten operation.  The returned value will be
  * used instead.
  */
-CPT(TransformState) BillboardEffect::
-prepare_flatten_transform(const TransformState *net_transform) const {
+Transform BillboardEffect::
+prepare_flatten_transform(const Transform &net_transform) const {
   // We don't want any flatten operation to rotate the billboarded node, since
   // the billboard effect should eat any rotation that comes in from above.
-  return net_transform->set_hpr(LVecBase3(0, 0, 0));
+  Transform result(net_transform);
+  result.set_hpr(LVecBase3(0, 0, 0));
+  return result;
 }
 
 /**
@@ -122,22 +124,21 @@ has_cull_callback() const {
  */
 void BillboardEffect::
 cull_callback(CullTraverser *trav, CullTraverserData &data,
-              CPT(TransformState) &node_transform,
-              CPT(RenderState) &) const {
-  CPT(TransformState) modelview_transform = data.get_modelview_transform(trav);
-  if (modelview_transform->is_singular()) {
+              Transform &node_transform, CPT(RenderState) &) const {
+  Transform modelview_transform = data.get_modelview_transform(trav);
+  /*if (modelview_transform->is_singular()) {
     // If we're under a singular transform, never mind.
     return;
-  }
+  }*/
 
   // Since the "modelview" transform from the cull traverser already includes
   // the inverse camera transform, the camera transform is identity.
-  CPT(TransformState) camera_transform = TransformState::make_identity();
+  Transform camera_transform = Transform::make_identity();
 
   // But if we're rotating to face something other than the camera, we have to
   // compute the "camera" transform to compensate for that.
   if (!_look_at.is_empty()) {
-    camera_transform = trav->get_camera_transform()->invert_compose(_look_at.get_net_transform());
+    camera_transform = trav->get_camera_transform().invert_compose(_look_at.get_net_transform());
   }
 
   if (data._instances == nullptr) {
@@ -149,15 +150,15 @@ cull_callback(CullTraverser *trav, CullTraverserData &data,
     data._instances = instances;
 
     for (InstanceList::Instance &instance : *instances) {
-      CPT(TransformState) inst_node_transform = node_transform;
-      CPT(TransformState) inst_modelview_transform = modelview_transform->compose(instance.get_transform());
+      Transform inst_node_transform = node_transform;
+      Transform inst_modelview_transform = modelview_transform.compose(instance.get_transform());
       compute_billboard(inst_node_transform, inst_modelview_transform, camera_transform);
 
-      instance.set_transform(instance.get_transform()->compose(inst_node_transform));
+      instance.set_transform(instance.get_transform().compose(inst_node_transform));
     }
 
     // We've already applied this onto the instances.
-    node_transform = TransformState::make_identity();
+    node_transform = Transform::make_identity();
   }
 }
 
@@ -183,8 +184,8 @@ has_adjust_transform() const {
  * they may (or may not) be modified in-place by the RenderEffect.
  */
 void BillboardEffect::
-adjust_transform(CPT(TransformState) &net_transform,
-                 CPT(TransformState) &node_transform,
+adjust_transform(Transform &net_transform,
+                 Transform &node_transform,
                  const PandaNode *) const {
   // A BillboardEffect can only affect the net transform when it is to a
   // particular node.  A billboard to a camera is camera-dependent, of course,
@@ -193,7 +194,7 @@ adjust_transform(CPT(TransformState) &net_transform,
     return;
   }
 
-  CPT(TransformState) camera_transform = _look_at.get_net_transform();
+  Transform camera_transform = _look_at.get_net_transform();
 
   compute_billboard(node_transform, net_transform, camera_transform);
 }
@@ -250,26 +251,20 @@ compare_to_impl(const RenderEffect *other) const {
  * The result is applied to node_transform, which is modified in-place.
  */
 void BillboardEffect::
-compute_billboard(CPT(TransformState) &node_transform,
-                  const TransformState *net_transform,
-                  const TransformState *camera_transform) const {
+compute_billboard(Transform &node_transform,
+                  const Transform &net_transform,
+                  const Transform &camera_transform) const {
   // First, extract out just the translation component of the node's local
   // transform.  This gets applied to the net transform, to compute the look-
   // at direction properly.
-  CPT(TransformState) translate = TransformState::make_pos(node_transform->get_pos());
+  Transform translate = Transform::make_pos(node_transform.get_pos());
 
   // And then the translation gets removed from the node, but we keep its
   // rotation etc., which gets applied after the billboard operation.
-  node_transform = node_transform->set_pos(LPoint3(0.0f, 0.0f, 0.0f));
-
-  CPT(TransformState) rel_transform =
-    net_transform->compose(translate)->invert_compose(camera_transform);
-  if (!rel_transform->has_mat()) {
-    // Never mind.
-    return;
-  }
+  node_transform.set_pos(LPoint3(0.0f, 0.0f, 0.0f));
 
-  const LMatrix4 &rel_mat = rel_transform->get_mat();
+  Transform rel_transform =
+    net_transform.compose(translate).invert_compose(camera_transform);
 
   // Determine the look_at point in the camera space.
   LVector3 camera_pos, up;
@@ -280,12 +275,12 @@ compute_billboard(CPT(TransformState) &node_transform,
   // direction, not directly to the camera.
 
   if (_eye_relative) {
-    up = _up_vector * rel_mat;
-    camera_pos = LVector3::forward() * rel_mat;
+    up = rel_transform.xform_vec(_up_vector);
+    camera_pos = rel_transform.xform_vec(LVector3::forward());
 
   } else {
     up = _up_vector;
-    camera_pos = -(_look_at_point * rel_mat);
+    camera_pos = -rel_transform.xform_point(_look_at_point);
   }
 
   // Now determine the rotation matrix for the Billboard.
@@ -299,10 +294,10 @@ compute_billboard(CPT(TransformState) &node_transform,
   // Also slide the billboard geometry towards the camera according to the
   // offset factor.
   if (_offset != 0.0f || _fixed_depth) {
-    LVector3 translate(rel_mat(3, 0), rel_mat(3, 1), rel_mat(3, 2));
+    LVector3 translate = rel_transform.get_pos();
     LPoint3 pos;
     if (_fixed_depth) {
-      pos = translate / rel_mat(3, 3);
+      pos = translate;
     } else {
       pos.fill(0.0f);
     }
@@ -311,7 +306,7 @@ compute_billboard(CPT(TransformState) &node_transform,
     rotate.set_row(3, pos + translate);
   }
 
-  node_transform = translate->compose(TransformState::make_mat(rotate))->compose(node_transform);
+  node_transform = translate.compose(Transform::make_mat(rotate)).compose(node_transform);
 }
 
 /**

+ 7 - 7
panda/src/pgraph/billboardEffect.h

@@ -51,26 +51,26 @@ PUBLISHED:
 
 public:
   virtual bool safe_to_transform() const;
-  virtual CPT(TransformState) prepare_flatten_transform(const TransformState *net_transform) const;
+  virtual Transform prepare_flatten_transform(const Transform &net_transform) const;
   virtual void output(std::ostream &out) const;
 
   virtual bool has_cull_callback() const;
   virtual void cull_callback(CullTraverser *trav, CullTraverserData &data,
-                             CPT(TransformState) &node_transform,
+                             Transform &node_transform,
                              CPT(RenderState) &node_state) const;
 
   virtual bool has_adjust_transform() const;
-  virtual void adjust_transform(CPT(TransformState) &net_transform,
-                                CPT(TransformState) &node_transform,
+  virtual void adjust_transform(Transform &net_transform,
+                                Transform &node_transform,
                                 const PandaNode *node) const;
 
 protected:
   virtual int compare_to_impl(const RenderEffect *other) const;
 
 private:
-  void compute_billboard(CPT(TransformState) &node_transform,
-                         const TransformState *net_transform,
-                         const TransformState *camera_transform) const;
+  void compute_billboard(Transform &node_transform,
+                         const Transform &net_transform,
+                         const Transform &camera_transform) const;
 
 private:
   bool _off;

+ 38 - 36
panda/src/pgraph/compassEffect.cxx

@@ -113,60 +113,59 @@ has_cull_callback() const {
  */
 void CompassEffect::
 cull_callback(CullTraverser *trav, CullTraverserData &data,
-              CPT(TransformState) &node_transform,
-              CPT(RenderState) &) const {
+              Transform &node_transform, CPT(RenderState) &) const {
   if (_properties == 0) {
     // Nothing to do.
     return;
   }
 
   if (data._instances == nullptr) {
-    CPT(TransformState) true_net_transform = data.get_net_transform(trav);
-    CPT(TransformState) want_net_transform = true_net_transform;
+    Transform true_net_transform = data.get_net_transform(trav);
+    Transform want_net_transform = true_net_transform;
     adjust_transform(want_net_transform, node_transform, data.node());
 
     // Now compute the transform that will convert true_net_transform to
     // want_transform.  This is inv(true_net_transform) * want_transform.
-    CPT(TransformState) compass_transform =
-      true_net_transform->invert_compose(want_net_transform);
+    Transform compass_transform =
+      true_net_transform.invert_compose(want_net_transform);
 
     // And modify our local node's apparent transform so that
     // true_net_transform->compose(new_node_transform) produces the same result
     // we would have gotten had we actually computed
     // want_transform->compose(orig_node_transform).
-    node_transform = compass_transform->compose(node_transform);
+    node_transform = compass_transform.compose(node_transform);
   }
   else {
     // Compute the billboard effect for every instance individually.
     InstanceList *instances = new InstanceList(*data._instances);
     data._instances = instances;
 
-    CPT(TransformState) parent_net_transform = data.get_net_transform(trav);
-    CPT(TransformState) invert_net_transform = parent_net_transform->get_inverse();
+    Transform parent_net_transform = data.get_net_transform(trav);
+    Transform invert_net_transform = parent_net_transform.get_inverse();
 
     // We make use of the fact that we know adjust_transform() does not modify
     // its node_transform parameter.
-    CPT(TransformState) node_transform_copy = node_transform;
-    if (node_transform_copy->is_identity()) {
+    Transform node_transform_copy = node_transform;
+    if (node_transform_copy.is_identity()) {
       // Slightly optimized case.
       for (InstanceList::Instance &instance : *instances) {
-        CPT(TransformState) true_net_transform = parent_net_transform->compose(instance.get_transform());
-        CPT(TransformState) want_net_transform = true_net_transform;
+        Transform true_net_transform = parent_net_transform.compose(instance.get_transform());
+        Transform want_net_transform = true_net_transform;
         adjust_transform(want_net_transform, node_transform_copy, data.node());
 
-        instance.set_transform(invert_net_transform->compose(want_net_transform));
+        instance.set_transform(invert_net_transform.compose(want_net_transform));
       }
     }
     else {
       // We apply the node_transform to the instances.
-      node_transform = TransformState::make_identity();
+      node_transform = Transform::make_identity();
 
       for (InstanceList::Instance &instance : *instances) {
-        CPT(TransformState) true_net_transform = parent_net_transform->compose(instance.get_transform());
-        CPT(TransformState) want_net_transform = true_net_transform;
+        Transform true_net_transform = parent_net_transform.compose(instance.get_transform());
+        Transform want_net_transform = true_net_transform;
         adjust_transform(want_net_transform, node_transform_copy, data.node());
 
-        instance.set_transform(invert_net_transform->compose(want_net_transform)->compose(node_transform_copy));
+        instance.set_transform(invert_net_transform.compose(want_net_transform).compose(node_transform_copy));
       }
     }
   }
@@ -191,8 +190,8 @@ has_adjust_transform() const {
  * they may (or may not) be modified in-place by the RenderEffect.
  */
 void CompassEffect::
-adjust_transform(CPT(TransformState) &net_transform,
-                 CPT(TransformState) &node_transform,
+adjust_transform(Transform &net_transform,
+                 Transform &node_transform,
                  const PandaNode *) const {
   if (_properties == 0) {
     // Nothing to do.
@@ -201,9 +200,9 @@ adjust_transform(CPT(TransformState) &net_transform,
 
   // The reference transform: where we are acting as if we inherit from.
   // Either the root node (identity) or the specified reference node.
-  CPT(TransformState) ref_transform;
+  Transform ref_transform;
   if (_reference.is_empty()) {
-    ref_transform = TransformState::make_identity();
+    ref_transform = Transform::make_identity();
   } else {
     ref_transform = _reference.get_net_transform();
   }
@@ -212,7 +211,7 @@ adjust_transform(CPT(TransformState) &net_transform,
   // of the components from the net transform we want to inherit normally from
   // our parent, with all of the components from the ref transform we want to
   // inherit from our reference.
-  CPT(TransformState) want_net_transform;
+  Transform want_net_transform;
   if (_properties == P_all) {
     // If we want to steal the whole transform, that's easy.
     want_net_transform = ref_transform;
@@ -220,8 +219,8 @@ adjust_transform(CPT(TransformState) &net_transform,
   } else {
     // How much of the pos do we want to steal?  We can always determine a
     // transform's pos, even if it's nondecomposable.
-    LVecBase3 want_pos = net_transform->get_pos();
-    const LVecBase3 &ref_pos = ref_transform->get_pos();
+    LVecBase3 want_pos = net_transform.get_pos();
+    const LVecBase3 &ref_pos = ref_transform.get_pos();
     if ((_properties & P_x) != 0) {
       want_pos[0] = ref_pos[0];
     }
@@ -234,30 +233,33 @@ adjust_transform(CPT(TransformState) &net_transform,
 
     if ((_properties & ~P_pos) == 0) {
       // If we only want to steal the pos, that's pretty easy.
-      want_net_transform = net_transform->set_pos(want_pos);
+      want_net_transform = net_transform;
+      want_net_transform.set_pos(want_pos);
 
     } else if ((_properties & (P_rot | P_scale)) == (P_rot | P_scale)) {
       // If we want to steal everything *but* the pos, also easy.
-      want_net_transform = ref_transform->set_pos(want_pos);
+      want_net_transform = ref_transform;
+      want_net_transform.set_pos(want_pos);
 
     } else {
       // For any other combination, we have to be able to decompose both
       // transforms.
-      if (!net_transform->has_components() ||
+      /*if (!net_transform->has_components() ||
           !ref_transform->has_components()) {
         // If we can't decompose, just do the best we can: steal everything
         // but the pos.
-        want_net_transform = ref_transform->set_pos(want_pos);
+        want_net_transform = ref_transform;
+        want_net_transform.set_pos(want_pos);
 
-      } else {
+      } else {*/
         // If we can decompose, then take only the components we want.
-        LQuaternion want_quat = net_transform->get_quat();
+        LQuaternion want_quat = net_transform.get_quat();
         if ((_properties & P_rot) != 0) {
-          want_quat = ref_transform->get_quat();
+          want_quat = ref_transform.get_quat();
         }
 
-        LVecBase3 want_scale = net_transform->get_scale();
-        const LVecBase3 &ref_scale = ref_transform->get_scale();
+        LVecBase3 want_scale = net_transform.get_scale();
+        LVecBase3 ref_scale = ref_transform.get_scale();
         if ((_properties & P_sx) != 0) {
           want_scale[0] = ref_scale[0];
         }
@@ -269,8 +271,8 @@ adjust_transform(CPT(TransformState) &net_transform,
         }
 
         want_net_transform =
-          TransformState::make_pos_quat_scale(want_pos, want_quat, want_scale);
-      }
+          Transform::make_pos_quat_scale(want_pos, want_quat, want_scale);
+      //}
     }
   }
 

+ 3 - 3
panda/src/pgraph/compassEffect.h

@@ -72,12 +72,12 @@ public:
 
   virtual bool has_cull_callback() const;
   virtual void cull_callback(CullTraverser *trav, CullTraverserData &data,
-                             CPT(TransformState) &node_transform,
+                             Transform &node_transform,
                              CPT(RenderState) &node_state) const;
 
   virtual bool has_adjust_transform() const;
-  virtual void adjust_transform(CPT(TransformState) &net_transform,
-                                CPT(TransformState) &node_transform,
+  virtual void adjust_transform(Transform &net_transform,
+                                Transform &node_transform,
                                 const PandaNode *node) const;
 
 protected:

+ 2 - 2
panda/src/pgraph/cullBin.h

@@ -21,11 +21,11 @@
 #include "pointerTo.h"
 #include "luse.h"
 #include "geomNode.h"
+#include "transform.h"
 
 class CullableObject;
 class GraphicsStateGuardianBase;
 class SceneSetup;
-class TransformState;
 class RenderState;
 class PandaNode;
 
@@ -84,7 +84,7 @@ protected:
 
   private:
     int _object_index;
-    CPT(TransformState) _current_transform;
+    Transform _current_transform;
     CPT(RenderState) _current_state;
     PT(PandaNode) _root_node;
     PT(GeomNode) _current_node;

+ 1 - 1
panda/src/pgraph/cullHandler.cxx

@@ -41,7 +41,7 @@ CullHandler::
  */
 void CullHandler::
 record_object(CullableObject *object, const CullTraverser *traverser) {
-  nout << *object->_geom << " " << *object->_internal_transform << " "
+  nout << *object->_geom << " " << object->_internal_transform << " "
        << *object->_state << "\n";
   delete object;
 }

+ 34 - 28
panda/src/pgraph/cullPlanes.cxx

@@ -98,7 +98,8 @@ apply_state(CPT(CullPlanes) &planes,
     }
   }
 
-  CPT(TransformState) net_transform = nullptr;
+  bool have_net_transform = false;
+  Transform net_transform;
 
   if (net_attrib != nullptr) {
     int num_on_planes = net_attrib->get_num_on_planes();
@@ -113,15 +114,16 @@ apply_state(CPT(CullPlanes) &planes,
         if (!off_attrib->has_off_plane(clip_plane)) {
           // Here's a new clip plane; add it to the list.  For this we need
           // the net transform to this node.
-          if (net_transform == nullptr) {
+          if (!have_net_transform) {
             net_transform = data->get_net_transform(trav);
+            have_net_transform = true;
           }
 
           PlaneNode *plane_node = DCAST(PlaneNode, clip_plane.node());
-          CPT(TransformState) new_transform =
-            net_transform->invert_compose(clip_plane.get_net_transform());
+          Transform new_transform =
+            net_transform.invert_compose(clip_plane.get_net_transform());
 
-          LPlane plane = plane_node->get_plane() * new_transform->get_mat();
+          LPlane plane = plane_node->get_plane() * new_transform.get_mat();
           new_planes->_planes[clip_plane] = new BoundingPlane(-plane);
         }
       }
@@ -129,7 +131,9 @@ apply_state(CPT(CullPlanes) &planes,
   }
 
   if (node_effect != nullptr) {
-    CPT(TransformState) center_transform = nullptr;
+    bool have_center_transform = false;
+    Transform center_transform;
+
     // We'll need to know the occluder's frustum in cull-center space.
     SceneSetup *scene = trav->get_scene();
     const Lens *lens = scene->get_lens();
@@ -147,16 +151,18 @@ apply_state(CPT(CullPlanes) &planes,
         OccluderNode *occluder_node = DCAST(OccluderNode, occluder.node());
         nassertv(occluder_node->get_num_vertices() == 4);
 
-        CPT(TransformState) occluder_transform = occluder.get_transform(scene->get_cull_center());
+        Transform occluder_transform = occluder.get_transform(scene->get_cull_center());
 
         // And the transform from cull-center space into the current node's
         // coordinate space.
-        if (center_transform == nullptr) {
-          if (net_transform == nullptr) {
+        if (!have_center_transform) {
+          if (!have_net_transform) {
             net_transform = data->get_net_transform(trav);
+            have_net_transform = true;
           }
 
-          center_transform = net_transform->invert_compose(scene->get_cull_center().get_net_transform());
+          center_transform = net_transform.invert_compose(scene->get_cull_center().get_net_transform());
+          have_center_transform = true;
         }
 
         // Compare the occluder node's bounding volume to the view frustum.
@@ -168,13 +174,12 @@ apply_state(CPT(CullPlanes) &planes,
         PT(BoundingBox) occluder_gbv;
         // Get a transform from the occluder directly to this node's space for
         // comparing with the current view frustum.
-        CPT(TransformState) composed_transform = center_transform->compose(occluder_transform);
-        const LMatrix4 &composed_mat = composed_transform->get_mat();
+        Transform composed_transform = center_transform.compose(occluder_transform);
         LPoint3 ccp[4];
-        ccp[0] = occluder_node->get_vertex(0) * composed_mat;
-        ccp[1] = occluder_node->get_vertex(1) * composed_mat;
-        ccp[2] = occluder_node->get_vertex(2) * composed_mat;
-        ccp[3] = occluder_node->get_vertex(3) * composed_mat;
+        ccp[0] = composed_transform.xform_point(occluder_node->get_vertex(0));
+        ccp[1] = composed_transform.xform_point(occluder_node->get_vertex(1));
+        ccp[2] = composed_transform.xform_point(occluder_node->get_vertex(2));
+        ccp[3] = composed_transform.xform_point(occluder_node->get_vertex(3));
 
         LPoint3 ccp_min(min(min(ccp[0][0], ccp[1][0]),
                      min(ccp[2][0], ccp[3][0])),
@@ -204,12 +209,11 @@ apply_state(CPT(CullPlanes) &planes,
         }
 
         // Get the occluder geometry in cull-center space.
-        const LMatrix4 &occluder_mat_cull = occluder_transform->get_mat();
         LPoint3 points_near[4];
-        points_near[0] = occluder_node->get_vertex(0) * occluder_mat_cull;
-        points_near[1] = occluder_node->get_vertex(1) * occluder_mat_cull;
-        points_near[2] = occluder_node->get_vertex(2) * occluder_mat_cull;
-        points_near[3] = occluder_node->get_vertex(3) * occluder_mat_cull;
+        points_near[0] = occluder_transform.xform_point(occluder_node->get_vertex(0));
+        points_near[1] = occluder_transform.xform_point(occluder_node->get_vertex(1));
+        points_near[2] = occluder_transform.xform_point(occluder_node->get_vertex(2));
+        points_near[3] = occluder_transform.xform_point(occluder_node->get_vertex(3));
         LPlane plane(points_near[0], points_near[1], points_near[2]);
 
         if (plane.get_normal().dot(LVector3::forward()) >= 0.0) {
@@ -277,15 +281,17 @@ apply_state(CPT(CullPlanes) &planes,
         // are fully contained within this new one.
 
         // Get the occluder coordinates in global space.
-        const LMatrix4 &occluder_mat = occluder.get_net_transform()->get_mat();
-        points_near[0] = occluder_node->get_vertex(0) * occluder_mat;
-        points_near[1] = occluder_node->get_vertex(1) * occluder_mat;
-        points_near[2] = occluder_node->get_vertex(2) * occluder_mat;
-        points_near[3] = occluder_node->get_vertex(3) * occluder_mat;
+        {
+          Transform transform = occluder.get_net_transform();
+          points_near[0] = transform.xform_point(occluder_node->get_vertex(0));
+          points_near[1] = transform.xform_point(occluder_node->get_vertex(1));
+          points_near[2] = transform.xform_point(occluder_node->get_vertex(2));
+          points_near[3] = transform.xform_point(occluder_node->get_vertex(3));
+        }
 
         // For the far points, project PAST the far clip of the lens to
         // ensures we get stuff that might be intersecting the far clip.
-        LPoint3 center = scene->get_cull_center().get_net_transform()->get_pos();
+        LPoint3 center = scene->get_cull_center().get_net_transform().get_pos();
         PN_stdfloat far_clip = scene->get_lens()->get_far() * 2.0;
         LPoint3 points_far[4];
         points_far[0] = normalize(points_near[0] - center) * far_clip + points_near[0];
@@ -303,7 +309,7 @@ apply_state(CPT(CullPlanes) &planes,
 
         if (show_occluder_volumes) {
           // Draw the frustum for visualization.
-          nassertv(net_transform != nullptr);
+          nassertv(have_net_transform);
           trav->draw_bounding_volume(frustum, data->get_internal_transform(trav));
         }
       }

+ 3 - 2
panda/src/pgraph/cullResult.cxx

@@ -118,9 +118,10 @@ add_object(CullableObject *object, const CullTraverser *traverser) {
   if (rescale->get_mode() == RescaleNormalAttrib::M_auto) {
     RescaleNormalAttrib::Mode mode;
 
-    if (object->_internal_transform->has_identity_scale()) {
+    LVecBase3 scale = object->_internal_transform.get_scale();
+    if (scale.almost_equal(LVecBase3(1, 1, 1))) {
       mode = RescaleNormalAttrib::M_none;
-    } else if (object->_internal_transform->has_uniform_scale()) {
+    } else if (scale[0] == scale[1] && scale[0] == scale[2]) {
       mode = RescaleNormalAttrib::M_rescale;
     } else {
       mode = RescaleNormalAttrib::M_normalize;

+ 0 - 1
panda/src/pgraph/cullResult.h

@@ -31,7 +31,6 @@ class CullTraverser;
 class GraphicsStateGuardianBase;
 class RenderState;
 class SceneSetup;
-class TransformState;
 
 /**
  * This stores the result of a BinCullHandler traversal: an ordered collection

+ 3 - 3
panda/src/pgraph/cullTraverser.I

@@ -57,7 +57,7 @@ get_tag_state_key() const {
 /**
  * Returns the position of the camera relative to the starting node.
  */
-INLINE const TransformState *CullTraverser::
+INLINE const Transform &CullTraverser::
 get_camera_transform() const {
   return _scene_setup->get_camera_transform();
 }
@@ -71,7 +71,7 @@ get_camera_transform() const {
  * transform of the current node use
  * CullTraverserData::get_modelview_transform().
  */
-INLINE const TransformState *CullTraverser::
+INLINE const Transform &CullTraverser::
 get_world_transform() const {
   return _scene_setup->get_world_transform();
 }
@@ -232,7 +232,7 @@ traverse_down(const CullTraverserData &data, PandaNode *node) {
  */
 INLINE void CullTraverser::
 traverse_down(const CullTraverserData &data, PandaNode *node,
-               const TransformState *net_transform, const RenderState *state) {
+               const Transform &net_transform, const RenderState *state) {
   PandaNodePipelineReader node_reader(node, data._node_reader.get_current_thread());
 
   int result = data.is_child_in_view(node_reader, _camera_mask);

+ 11 - 12
panda/src/pgraph/cullTraverser.cxx

@@ -14,7 +14,6 @@
 #include "config_pgraph.h"
 #include "cullTraverser.h"
 #include "cullTraverserData.h"
-#include "transformState.h"
 #include "renderState.h"
 #include "colorAttrib.h"
 #include "textureAttrib.h"
@@ -136,7 +135,7 @@ traverse(const NodePath &root) {
     // Store this pointer in this
     set_portal_clipper(&portal_viewer);
 
-    CullTraverserData data(root, TransformState::make_identity(),
+    CullTraverserData data(root, Transform::make_identity(),
                            _initial_state, _view_frustum,
                            _current_thread);
 
@@ -151,17 +150,17 @@ traverse(const NodePath &root) {
 
     // Render the frustum relative to the cull center.
     NodePath cull_center = _scene_setup->get_cull_center();
-    CPT(TransformState) transform = cull_center.get_transform(root);
+    Transform transform = cull_center.get_transform(root);
 
     CullTraverserData my_data(data, portal_viewer._previous,
-                              data._net_transform->compose(transform),
+                              data._net_transform.compose(transform),
                               data._state, data._view_frustum);
     if (my_data.is_in_view(_camera_mask)) {
       do_traverse(my_data);
     }
 
   } else {
-    CullTraverserData data(root, TransformState::make_identity(),
+    CullTraverserData data(root, Transform::make_identity(),
                            _initial_state, _view_frustum,
                            _current_thread);
 
@@ -254,7 +253,7 @@ end_traverse() {
  */
 void CullTraverser::
 draw_bounding_volume(const BoundingVolume *vol,
-                     const TransformState *internal_transform) const {
+                     const Transform &internal_transform) const {
   PT(Geom) bounds_viz = make_bounds_viz(vol);
 
   if (bounds_viz != nullptr) {
@@ -276,7 +275,7 @@ draw_bounding_volume(const BoundingVolume *vol,
  */
 void CullTraverser::
 do_fake_cull(const CullTraverserData &data, PandaNode *child,
-             const TransformState *net_transform, const RenderState *state) {
+             const Transform &net_transform, const RenderState *state) {
 #ifndef NDEBUG
   // Once someone asks for this pointer, we hold its reference count and never
   // free it.
@@ -302,7 +301,7 @@ do_fake_cull(const CullTraverserData &data, PandaNode *child,
 void CullTraverser::
 show_bounds(CullTraverserData &data, bool tight) {
   PandaNode *node = data.node();
-  CPT(TransformState) internal_transform = data.get_internal_transform(this);
+  Transform internal_transform = data.get_internal_transform(this);
 
   if (tight) {
     PT(Geom) bounds_viz = make_tight_bounds_viz(node);
@@ -320,7 +319,7 @@ show_bounds(CullTraverserData &data, bool tight) {
 
     if (node->is_geom_node()) {
       // Also show the bounding volumes of included Geoms.
-      internal_transform = internal_transform->compose(node->get_transform());
+      internal_transform = internal_transform.compose(node->get_transform());
       GeomNode *gnode = (GeomNode *)node;
       int num_geoms = gnode->get_num_geoms();
       for (int i = 0; i < num_geoms; ++i) {
@@ -331,12 +330,12 @@ show_bounds(CullTraverserData &data, bool tight) {
   } else {
     // Draw bounds for every instance.
     for (const InstanceList::Instance &instance : *data._instances) {
-      CPT(TransformState) transform = internal_transform->compose(instance.get_transform());
+      Transform transform = internal_transform.compose(instance.get_transform());
       draw_bounding_volume(node->get_bounds(), transform);
 
       if (node->is_geom_node()) {
         // Also show the bounding volumes of included Geoms.
-        transform = transform->compose(node->get_transform());
+        transform = transform.compose(node->get_transform());
         GeomNode *gnode = (GeomNode *)node;
         int num_geoms = gnode->get_num_geoms();
         for (int i = 0; i < num_geoms; ++i) {
@@ -474,7 +473,7 @@ make_tight_bounds_viz(PandaNode *node) const {
 
   LPoint3 n, x;
   bool found_any = false;
-  node->calc_tight_bounds(n, x, found_any, TransformState::make_identity(),
+  node->calc_tight_bounds(n, x, found_any, Transform::make_identity(),
                           _current_thread);
   if (found_any) {
     PT(GeomVertexData) vdata = new GeomVertexData

+ 6 - 6
panda/src/pgraph/cullTraverser.h

@@ -19,7 +19,7 @@
 #include "geom.h"
 #include "sceneSetup.h"
 #include "renderState.h"
-#include "transformState.h"
+#include "transform.h"
 #include "geometricBoundingVolume.h"
 #include "pointerTo.h"
 #include "camera.h"
@@ -60,8 +60,8 @@ PUBLISHED:
   INLINE void set_camera_mask(const DrawMask &camera_mask);
   INLINE const DrawMask &get_camera_mask() const;
 
-  INLINE const TransformState *get_camera_transform() const;
-  INLINE const TransformState *get_world_transform() const;
+  INLINE const Transform &get_camera_transform() const;
+  INLINE const Transform &get_world_transform() const;
 
   INLINE const RenderState *get_initial_state() const;
   INLINE bool get_depth_offset_decals() const;
@@ -87,12 +87,12 @@ PUBLISHED:
   virtual void end_traverse();
 
   void draw_bounding_volume(const BoundingVolume *vol,
-                            const TransformState *internal_transform) const;
+                            const Transform &internal_transform) const;
 
 public:
   INLINE void traverse_down(const CullTraverserData &data, PandaNode *child);
   INLINE void traverse_down(const CullTraverserData &data, PandaNode *child,
-                            const TransformState *net_transform,
+                            const Transform &net_transform,
                             const RenderState *state);
   INLINE void traverse_down(const CullTraverserData &data,
                             const PandaNode::DownConnection &child);
@@ -101,7 +101,7 @@ public:
                             const RenderState *state);
 
   void do_fake_cull(const CullTraverserData &data, PandaNode *child,
-                    const TransformState *net_transform,
+                    const Transform &net_transform,
                     const RenderState *state);
 
 public:

+ 11 - 11
panda/src/pgraph/cullTraverserData.I

@@ -16,7 +16,7 @@
  */
 INLINE CullTraverserData::
 CullTraverserData(const NodePath &start,
-                  const TransformState *net_transform,
+                  const Transform &net_transform,
                   CPT(RenderState) state,
                   GeometricBoundingVolume *view_frustum,
                   Thread *current_thread) :
@@ -37,7 +37,7 @@ CullTraverserData(const NodePath &start,
  */
 INLINE CullTraverserData::
 CullTraverserData(const CullTraverserData &parent, PandaNode *child,
-                  const TransformState *net_transform,
+                  const Transform &net_transform,
                   CPT(RenderState) state,
                   GeometricBoundingVolume *view_frustum) :
   _next(&parent),
@@ -61,7 +61,7 @@ CullTraverserData(const CullTraverserData &parent, PandaNode *child,
 INLINE CullTraverserData::
 CullTraverserData(const CullTraverserData &parent,
                   PandaNodePipelineReader &&node_reader,
-                  const TransformState *net_transform,
+                  const Transform &net_transform,
                   CPT(RenderState) state,
                   GeometricBoundingVolume *view_frustum) :
   _next(&parent),
@@ -138,26 +138,26 @@ set_view_frustum(PT(GeometricBoundingVolume) view_frustum) {
  * Returns the modelview transform: the relative transform from the camera to
  * the model.
  */
-INLINE CPT(TransformState) CullTraverserData::
+INLINE Transform CullTraverserData::
 get_modelview_transform(const CullTraverser *trav) const {
-  return trav->get_world_transform()->compose(_net_transform);
+  return trav->get_world_transform().compose(_net_transform);
 }
 
 /**
  * Returns the internal transform: the modelview transform in the GSG's
  * internal coordinate system.
  */
-INLINE CPT(TransformState) CullTraverserData::
+INLINE Transform CullTraverserData::
 get_internal_transform(const CullTraverser *trav) const {
-  const TransformState *cs_world_transform = trav->get_scene()->get_cs_world_transform();
-  return cs_world_transform->compose(_net_transform);
+  const Transform &cs_world_transform = trav->get_scene()->get_cs_world_transform();
+  return cs_world_transform.compose(_net_transform);
 }
 
 /**
  * Returns the net transform: the relative transform from root of the scene
  * graph to the current node.
  */
-INLINE const TransformState *CullTraverserData::
+INLINE const Transform &CullTraverserData::
 get_net_transform(const CullTraverser *) const {
   return _net_transform;
 }
@@ -176,7 +176,7 @@ is_in_view(const DrawMask &camera_mask) const {
     return BoundingVolume::IF_no_intersection;
   }
 
-  if (_node_reader.get_transform()->is_invalid()) {
+  if (_node_reader.get_transform().is_invalid()) {
     // If the transform is invalid, forget it.
     return BoundingVolume::IF_no_intersection;
   }
@@ -205,7 +205,7 @@ is_child_in_view(const PandaNodePipelineReader &node_reader, const DrawMask &cam
     return BoundingVolume::IF_no_intersection;
   }
 
-  if (_node_reader.get_transform()->is_invalid()) {
+  if (_node_reader.get_transform().is_invalid()) {
     // If the transform is invalid, forget it.
     return BoundingVolume::IF_no_intersection;
   }

+ 27 - 40
panda/src/pgraph/cullTraverserData.cxx

@@ -48,7 +48,7 @@ apply_transform_and_state(CullTraverser *trav) {
     apply_transform(_node_reader.get_transform());
   } else {
     // The cull callback may decide to modify the node_transform.
-    CPT(TransformState) node_transform = _node_reader.get_transform();
+    Transform node_transform = _node_reader.get_transform();
     node_effects->cull_callback(trav, *this, node_transform, node_state);
     apply_transform(node_transform);
 
@@ -94,44 +94,35 @@ apply_transform_and_state(CullTraverser *trav) {
  * Applies the indicated transform changes onto the current data.
  */
 void CullTraverserData::
-apply_transform(const TransformState *node_transform) {
-  if (!node_transform->is_identity()) {
+apply_transform(const Transform &node_transform) {
+  if (!node_transform.is_identity()) {
     if (_instances != nullptr) {
       InstanceList *instances = new InstanceList(*_instances);
       for (InstanceList::Instance &instance : *instances) {
-        instance.set_transform(instance.get_transform()->compose(node_transform));
+        instance.set_transform(instance.get_transform().compose(node_transform));
       }
       _instances = std::move(instances);
       return;
     }
 
-    _net_transform = _net_transform->compose(node_transform);
+    _net_transform = _net_transform.compose(node_transform);
 
     if (_view_frustum != nullptr || _cull_planes != nullptr) {
       // We need to move the viewing frustums into the node's coordinate space
       // by applying the node's inverse transform.
-      const LMatrix4 *inverse_mat = node_transform->get_inverse_mat();
-      if (inverse_mat != nullptr) {
-        // Copy the bounding volumes for the frustums so we can transform
-        // them.
-        if (_view_frustum != nullptr) {
-          _view_frustum = _view_frustum->make_copy()->as_geometric_bounding_volume();
-          nassertv(_view_frustum != nullptr);
-
-          _view_frustum->xform(*inverse_mat);
-        }
-
-        if (_cull_planes != nullptr) {
-          _cull_planes = _cull_planes->xform(*inverse_mat);
-        }
+      LMatrix4 inverse_mat = node_transform.get_inverse().get_mat();
+
+      // Copy the bounding volumes for the frustums so we can transform
+      // them.
+      if (_view_frustum != nullptr) {
+        _view_frustum = _view_frustum->make_copy()->as_geometric_bounding_volume();
+        nassertv(_view_frustum != nullptr);
+
+        _view_frustum->xform(inverse_mat);
       }
-      else {
-        // But we can't invert a singular transform!  Instead of trying, we'll
-        // just give up on frustum culling from this point down.
-        pgraph_cat.warning()
-          << "Singular transformation detected on node: " << get_node_path() << "\n";
-        _view_frustum = nullptr;
-        _cull_planes = nullptr;
+
+      if (_cull_planes != nullptr) {
+        _cull_planes = _cull_planes->xform(inverse_mat);
       }
     }
   }
@@ -142,26 +133,22 @@ apply_transform(const TransformState *node_transform) {
  * in view if first transformed by the given transform, false otherwise.
  */
 bool CullTraverserData::
-is_instance_in_view(const TransformState *instance_transform, const DrawMask &camera_mask) const {
+is_instance_in_view(const Transform &instance_transform, const DrawMask &camera_mask) const {
   PT(GeometricBoundingVolume) view_frustum_p;
   const GeometricBoundingVolume *view_frustum = nullptr;
 
   if (_view_frustum != nullptr) {
-    if (!instance_transform->is_identity()) {
+    if (!instance_transform.is_identity()) {
       // We need to move the viewing frustums into the node's coordinate space
       // by applying the node's inverse transform.
-      const LMatrix4 *inverse_mat = instance_transform->get_inverse_mat();
-      if (inverse_mat != nullptr) {
-        // Copy the bounding volumes for the frustums so we can transform them.
-        view_frustum_p = _view_frustum->make_copy()->as_geometric_bounding_volume();
-        nassertr(view_frustum_p != nullptr, false);
-
-        view_frustum_p->xform(*inverse_mat);
-        view_frustum = view_frustum_p;
-      } else {
-        // Don't render instances with a singular transformation.
-        return false;
-      }
+      LMatrix4 inverse_mat = instance_transform.get_inverse().get_mat();
+
+      // Copy the bounding volumes for the frustums so we can transform them.
+      view_frustum_p = _view_frustum->make_copy()->as_geometric_bounding_volume();
+      nassertr(view_frustum_p != nullptr, false);
+
+      view_frustum_p->xform(inverse_mat);
+      view_frustum = view_frustum_p;
     } else {
       view_frustum = _view_frustum;
     }

+ 10 - 10
panda/src/pgraph/cullTraverserData.h

@@ -18,7 +18,7 @@
 #include "cullPlanes.h"
 #include "workingNodePath.h"
 #include "renderState.h"
-#include "transformState.h"
+#include "transform.h"
 #include "geometricBoundingVolume.h"
 #include "pointerTo.h"
 #include "drawMask.h"
@@ -41,18 +41,18 @@ class CullTraverser;
 class EXPCL_PANDA_PGRAPH CullTraverserData {
 public:
   INLINE CullTraverserData(const NodePath &start,
-                           const TransformState *net_transform,
+                           const Transform &net_transform,
                            CPT(RenderState) state,
                            GeometricBoundingVolume *view_frustum,
                            Thread *current_thread);
   INLINE CullTraverserData(const CullTraverserData &parent,
                            PandaNode *child,
-                           const TransformState *net_transform,
+                           const Transform &net_transform,
                            CPT(RenderState) state,
                            GeometricBoundingVolume *view_frustum);
   INLINE CullTraverserData(const CullTraverserData &parent,
                            PandaNodePipelineReader &&node_reader,
-                           const TransformState *net_transform,
+                           const Transform &net_transform,
                            CPT(RenderState) state,
                            GeometricBoundingVolume *view_frustum);
 
@@ -69,9 +69,9 @@ public:
   INLINE void set_view_frustum(PT(GeometricBoundingVolume) view_frustum);
 
 PUBLISHED:
-  INLINE CPT(TransformState) get_modelview_transform(const CullTraverser *trav) const;
-  INLINE CPT(TransformState) get_internal_transform(const CullTraverser *trav) const;
-  INLINE const TransformState *get_net_transform(const CullTraverser *trav) const;
+  INLINE Transform get_modelview_transform(const CullTraverser *trav) const;
+  INLINE Transform get_internal_transform(const CullTraverser *trav) const;
+  INLINE const Transform &get_net_transform(const CullTraverser *trav) const;
 
   INLINE int is_in_view(const DrawMask &camera_mask) const;
   INLINE bool is_this_node_hidden(const DrawMask &camera_mask) const;
@@ -79,13 +79,13 @@ PUBLISHED:
   bool apply_cull_planes(const CullPlanes *planes, const GeometricBoundingVolume *node_gbv);
 
   void apply_transform_and_state(CullTraverser *trav);
-  void apply_transform(const TransformState *node_transform);
+  void apply_transform(const Transform &node_transform);
 
   MAKE_PROPERTY(node_path, get_node_path);
   MAKE_PROPERTY(view_frustum, get_view_frustum, set_view_frustum);
 
 public:
-  bool is_instance_in_view(const TransformState *instance_transform, const DrawMask &camera_mask) const;
+  bool is_instance_in_view(const Transform &instance_transform, const DrawMask &camera_mask) const;
   INLINE int is_child_in_view(const PandaNodePipelineReader &node_reader, const DrawMask &camera_mask) const;
   INLINE int is_child_in_view(const PandaNode::DownConnection &child, const DrawMask &camera_mask) const;
 
@@ -97,7 +97,7 @@ private:
 
 public:
   PandaNodePipelineReader _node_reader;
-  CPT(TransformState) _net_transform;
+  Transform _net_transform;
   CPT(RenderState) _state;
   PT(GeometricBoundingVolume) _view_frustum;
   CPT(CullPlanes) _cull_planes;

部分文件因文件數量過多而無法顯示