Răsfoiți Sursa

introduce 2-d TransformStates

David Rose 20 ani în urmă
părinte
comite
65458d9edd

+ 5 - 19
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -2242,21 +2242,6 @@ do_issue_texture() {
     TexGenAttrib::Mode mode = _current_tex_gen->get_mode(stage);
     bool any_point_sprite = false;
 
-    // These transforms are used in the below to invert certain
-    // computed component values in various modes, to emulate the
-    // behavior of OpenGL, so we get a consistent behavior between the
-    // two of them.
-    static CPT(TransformState) invert_z =
-      TransformState::make_mat(LMatrix4f(1.0f, 0.0f, 0.0f, 0.0f,
-                                         0.0f, 1.0f, 0.0f, 0.0f,
-                                         0.0f, 0.0f, -1.0f, 0.0f,
-                                         0.0f, 0.0f, 0.0f, 1.0f));
-    static CPT(TransformState) invert_y =
-      TransformState::make_mat(LMatrix4f(1.0f, 0.0f, 0.0f, 0.0f,
-                                         0.0f, -1.0f, 0.0f, 0.0f,
-                                         0.0f, 0.0f, 1.0f, 0.0f,
-                                         0.0f, 0.0f, 0.0f, 1.0f));
-
     switch (mode) {
     case TexGenAttrib::M_off:
     case TexGenAttrib::M_light_vector:
@@ -2352,18 +2337,19 @@ do_issue_texture() {
     _d3d_device->SetRenderState(D3DRS_POINTSPRITEENABLE, any_point_sprite);
 
     if (!tex_mat->is_identity()) {
-      LMatrix4f m = tex_mat->get_mat();
-      _d3d_device->SetTransform(get_tex_mat_sym(i), (D3DMATRIX *)m.get_data());
-
-      if (!texcoords_3d) {
+      if (tex_mat->is_2d() && !texcoords_3d) {
         // For 2-d texture coordinates, we have to reorder the matrix.
+        LMatrix4f m = tex_mat->get_mat();
         m.set(m(0, 0), m(0, 1), m(0, 3), 0.0f,
               m(1, 0), m(1, 1), m(1, 3), 0.0f,
               m(3, 0), m(3, 1), m(3, 3), 0.0f,
               0.0f, 0.0f, 0.0f, 1.0f);
+        _d3d_device->SetTransform(get_tex_mat_sym(i), (D3DMATRIX *)m.get_data());
         _d3d_device->SetTextureStageState(i, D3DTSS_TEXTURETRANSFORMFLAGS,
                                           D3DTTFF_COUNT2);
       } else {
+        LMatrix4f m = tex_mat->get_mat();
+        _d3d_device->SetTransform(get_tex_mat_sym(i), (D3DMATRIX *)m.get_data());
         _d3d_device->SetTextureStageState(i, D3DTSS_TEXTURETRANSFORMFLAGS,
                                           D3DTTFF_COUNT3);
       }

+ 12 - 16
panda/src/pgraph/nodePath.I

@@ -1314,7 +1314,7 @@ INLINE void NodePath::
 set_tex_offset(TextureStage *stage, float u, float v) {
   nassertv_always(!is_empty());
   set_tex_transform(stage, 
-                    get_tex_transform(stage)->set_pos(LVecBase3f(u, v, 0.0f)));
+                    get_tex_transform(stage)->set_pos2d(LVecBase2f(u, v)));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1328,7 +1328,7 @@ INLINE void NodePath::
 set_tex_rotate(TextureStage *stage, float r) {
   nassertv_always(!is_empty());
   set_tex_transform(stage, 
-                    get_tex_transform(stage)->set_hpr(LVecBase3f(r, 0.0f, 0.0f)));
+                    get_tex_transform(stage)->set_rotate2d(r));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1352,7 +1352,7 @@ INLINE void NodePath::
 set_tex_scale(TextureStage *stage, float su, float sv) {
   nassertv_always(!is_empty());
   set_tex_transform(stage, 
-                    get_tex_transform(stage)->set_scale(LVecBase3f(su, sv, 1.0f)));
+                    get_tex_transform(stage)->set_scale2d(LVecBase2f(su, sv)));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1364,8 +1364,7 @@ set_tex_scale(TextureStage *stage, float su, float sv) {
 INLINE LVecBase2f NodePath::
 get_tex_offset(TextureStage *stage) const {
   nassertr_always(!is_empty(), LVecBase2f::zero());
-  const LVecBase3f &pos = get_tex_transform(stage)->get_pos();
-  return LVecBase2f(pos[0], pos[1]);
+  return get_tex_transform(stage)->get_pos2d();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1377,7 +1376,7 @@ get_tex_offset(TextureStage *stage) const {
 INLINE float NodePath::
 get_tex_rotate(TextureStage *stage) const {
   nassertr_always(!is_empty(), 0.0f);
-  return get_tex_transform(stage)->get_hpr()[0];
+  return get_tex_transform(stage)->get_rotate2d();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1389,8 +1388,7 @@ get_tex_rotate(TextureStage *stage) const {
 INLINE LVecBase2f NodePath::
 get_tex_scale(TextureStage *stage) const {
   nassertr_always(!is_empty(), LVecBase2f(1.0f, 1.0f));
-  const LVecBase3f &scale = get_tex_transform(stage)->get_scale();
-  return LVecBase2f(scale[0], scale[1]);
+  return get_tex_transform(stage)->get_scale2d();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1414,7 +1412,7 @@ INLINE void NodePath::
 set_tex_offset(const NodePath &other, TextureStage *stage, float u, float v) {
   nassertv_always(!is_empty());
   set_tex_transform(other, stage, 
-                    get_tex_transform(other, stage)->set_pos(LVecBase3f(u, v, 0.0f)));
+                    get_tex_transform(other, stage)->set_pos2d(LVecBase2f(u, v)));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1428,7 +1426,7 @@ INLINE void NodePath::
 set_tex_rotate(const NodePath &other, TextureStage *stage, float r) {
   nassertv_always(!is_empty());
   set_tex_transform(other, stage, 
-                    get_tex_transform(other, stage)->set_hpr(LVecBase3f(r, 0.0f, 0.0f)));
+                    get_tex_transform(other, stage)->set_rotate2d(r));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1452,7 +1450,7 @@ INLINE void NodePath::
 set_tex_scale(const NodePath &other, TextureStage *stage, float su, float sv) {
   nassertv_always(!is_empty());
   set_tex_transform(other, stage, 
-                    get_tex_transform(stage)->set_scale(LVecBase3f(su, sv, 1.0f)));
+                    get_tex_transform(stage)->set_scale2d(LVecBase2f(su, sv)));
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1464,8 +1462,7 @@ set_tex_scale(const NodePath &other, TextureStage *stage, float su, float sv) {
 INLINE LVecBase2f NodePath::
 get_tex_offset(const NodePath &other, TextureStage *stage) const {
   nassertr_always(!is_empty(), LVecBase2f::zero());
-  const LVecBase3f &pos = get_tex_transform(other, stage)->get_pos();
-  return LVecBase2f(pos[0], pos[1]);
+  return get_tex_transform(other, stage)->get_pos2d();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1477,7 +1474,7 @@ get_tex_offset(const NodePath &other, TextureStage *stage) const {
 INLINE float NodePath::
 get_tex_rotate(const NodePath &other, TextureStage *stage) const {
   nassertr_always(!is_empty(), 0.0f);
-  return get_tex_transform(other, stage)->get_hpr()[0];
+  return get_tex_transform(other, stage)->get_rotate2d();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1489,8 +1486,7 @@ get_tex_rotate(const NodePath &other, TextureStage *stage) const {
 INLINE LVecBase2f NodePath::
 get_tex_scale(const NodePath &other, TextureStage *stage) const {
   nassertr_always(!is_empty(), LVecBase2f(1.0f, 1.0f));
-  const LVecBase3f &scale = get_tex_transform(other, stage)->get_scale();
-  return LVecBase2f(scale[0], scale[1]);
+  return get_tex_transform(other, stage)->get_scale2d();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 4 - 15
panda/src/pgraph/texMatrixAttrib.cxx

@@ -57,10 +57,13 @@ make() {
 //     Function: TexMatrixAttrib::make
 //       Access: Published, Static
 //  Description: Constructs a TexMatrixAttrib that applies the
-//               indicated matrix to the default texture stage.
+//               indicated matrix to the default texture stage.  This
+//               interface is deprecated.
 ////////////////////////////////////////////////////////////////////
 CPT(RenderAttrib) TexMatrixAttrib::
 make(const LMatrix4f &mat) {
+  pgraph_cat.warning()
+    << "Using deprecated TexMatrixAttrib interface.\n";
   if (mat == LMatrix4f::ident_mat()) {
     return make();
   }
@@ -68,20 +71,6 @@ make(const LMatrix4f &mat) {
   return make(TextureStage::get_default(), transform);
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: TexMatrixAttrib::make
-//       Access: Published, Static
-//  Description: Constructs a TexMatrixAttrib that applies the
-//               indicated transform to the default texture stage.
-////////////////////////////////////////////////////////////////////
-CPT(RenderAttrib) TexMatrixAttrib::
-make(const TransformState *transform) {
-  if (transform->is_identity()) {
-    return make();
-  }
-  return make(TextureStage::get_default(), transform);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: TexMatrixAttrib::make
 //       Access: Published, Static

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

@@ -45,7 +45,6 @@ public:
 PUBLISHED:
   static CPT(RenderAttrib) make();
   static CPT(RenderAttrib) make(const LMatrix4f &mat);
-  static CPT(RenderAttrib) make(const TransformState *transform);
   static CPT(RenderAttrib) make(TextureStage *stage, const TransformState *transform);
 
   CPT(RenderAttrib) add_stage(TextureStage *stage, const TransformState *transform) const;

+ 204 - 3
panda/src/pgraph/transformState.I

@@ -76,9 +76,9 @@ make_pos_hpr(const LVecBase3f &pos, const LVecBase3f &hpr) {
 ////////////////////////////////////////////////////////////////////
 INLINE CPT(TransformState) TransformState::
 make_scale(float scale) {
-  return make_pos_hpr_scale(LVecBase3f(0.0f, 0.0f, 0.0f),
-                            LVecBase3f(0.0f, 0.0f, 0.0f),
-                            LVecBase3f(scale, scale, scale));
+  // We actually map this 3-d uniform make_scale() to the 2-d
+  // version--might as well call it a 2-d scale.
+  return make_scale2d(scale);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -132,6 +132,88 @@ make_pos_quat_scale(const LVecBase3f &pos, const LQuaternionf &quat,
   return make_pos_quat_scale_shear(pos, quat, scale, LVecBase3f::zero());
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_pos2d
+//       Access: Published, Static
+//  Description: Makes a new 2-d TransformState with the specified
+//               components.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(TransformState) TransformState::
+make_pos2d(const LVecBase2f &pos) {
+  return make_pos_rotate_scale2d(pos, 0.0f, LVecBase2f(1.0f, 1.0f));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_rotate2d
+//       Access: Published, Static
+//  Description: Makes a new 2-d TransformState with the specified
+//               components.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(TransformState) TransformState::
+make_rotate2d(float rotate) {
+  return make_pos_rotate_scale2d(LVecBase2f(0.0f, 0.0f), rotate, 
+                                 LVecBase2f(1.0f, 1.0f));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_pos_rotate2d
+//       Access: Published, Static
+//  Description: Makes a new 2-d TransformState with the specified
+//               components.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(TransformState) TransformState::
+make_pos_rotate2d(const LVecBase2f &pos, float rotate) {
+  return make_pos_rotate_scale2d(pos, rotate,
+                                 LVecBase2f(1.0, 1.0f));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_scale2d
+//       Access: Published, Static
+//  Description: Makes a new 2-d TransformState with the specified
+//               components.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(TransformState) TransformState::
+make_scale2d(float scale) {
+  return make_pos_rotate_scale2d(LVecBase2f(0.0f, 0.0f), 0.0f,
+                                 LVecBase2f(scale, scale));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_scale2d
+//       Access: Published, Static
+//  Description: Makes a new 2-d TransformState with the specified
+//               components.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(TransformState) TransformState::
+make_scale2d(const LVecBase2f &scale) {
+  return make_pos_rotate_scale2d(LVecBase2f(0.0f, 0.0f), 0.0f, scale);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_shear2d
+//       Access: Published, Static
+//  Description: Makes a new 2-d TransformState with the specified
+//               components.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(TransformState) TransformState::
+make_shear2d(float shear) {
+  return make_pos_rotate_scale_shear2d(LVecBase2f(0.0f, 0.0f), 0.0f, 
+                                       LVecBase2f(1.0f, 1.0f), shear);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_pos_rotate_scale2d
+//       Access: Published, Static
+//  Description: Makes a new 2-d TransformState with the specified
+//               components.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(TransformState) TransformState::
+make_pos_rotate_scale2d(const LVecBase2f &pos, float rotate, 
+                        const LVecBase2f &scale) {
+  return make_pos_rotate_scale_shear2d(pos, rotate, scale, 0.0f);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::is_identity
 //       Access: Published
@@ -168,6 +250,19 @@ is_singular() const {
   return ((_flags & F_is_singular) != 0);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::is_2d
+//       Access: Published
+//  Description: Returns true if the transform has been constructed
+//               entirely using the 2-d transform operations,
+//               e.g. make_pos2d(), and therefore operates strictly in
+//               two-dimensional space on X and Y only.
+////////////////////////////////////////////////////////////////////
+INLINE bool TransformState::
+is_2d() const {
+  return ((_flags & F_is_2d) != 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::has_components
 //       Access: Published
@@ -446,6 +541,89 @@ get_mat() const {
   return _mat;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::get_pos2d
+//       Access: Published
+//  Description: Returns the pos component of the 2-d transform.  It
+//               is an error to call this if has_pos() or is_2d()
+//               returned false.
+////////////////////////////////////////////////////////////////////
+INLINE LVecBase2f TransformState::
+get_pos2d() const {
+  check_components();
+  nassertr(has_pos() && is_2d(), LVecBase2f::zero());
+  return LVecBase2f(_pos[0], _pos[1]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::get_rotate2d
+//       Access: Published
+//  Description: Returns the rotation component of the 2-d transform
+//               as an angle in degrees clockwise about the origin.
+//               It is an error to call this if has_components() or
+//               is_2d() returned false.
+////////////////////////////////////////////////////////////////////
+INLINE float TransformState::
+get_rotate2d() const {
+  check_hpr();
+  nassertr(!is_invalid() && is_2d(), 0);
+  switch (get_default_coordinate_system()) {
+  default:
+  case CS_zup_right:
+    return _hpr[0];
+  case CS_zup_left:
+    return -_hpr[0];
+  case CS_yup_right:
+    return -_hpr[2];
+  case CS_yup_left:
+    return _hpr[2];
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::get_scale2d
+//       Access: Published
+//  Description: Returns the scale component of the 2-d transform.  It
+//               is an error to call this if has_components() or
+//               is_2d() returned false.
+////////////////////////////////////////////////////////////////////
+INLINE LVecBase2f TransformState::
+get_scale2d() const {
+  check_components();
+  nassertr(!is_invalid() && is_2d(), LVecBase2f::zero());
+  return LVecBase2f(_scale[0], _scale[1]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::get_shear2d
+//       Access: Published
+//  Description: Returns the shear component of the 2-d transform.  It
+//               is an error to call this if has_components() or
+//               is_2d() returned false.
+////////////////////////////////////////////////////////////////////
+INLINE float TransformState::
+get_shear2d() const {
+  check_components();
+  nassertr(!is_invalid() && is_2d(), 0.0f);
+  return _shear[0];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::get_mat3
+//       Access: Published
+//  Description: Returns the 3x3 matrix that describes the 2-d
+//               transform.  It is an error to call this if is_2d()
+//               returned false.
+////////////////////////////////////////////////////////////////////
+INLINE LMatrix3f TransformState::
+get_mat3() const {
+  nassertr(has_mat() && is_2d(), LMatrix3f::ident_mat());
+  check_mat();
+  return LMatrix3f(_mat(0, 0), _mat(0, 1), _mat(0, 3),
+                   _mat(1, 0), _mat(1, 1), _mat(1, 3),
+                   _mat(3, 0), _mat(3, 1), _mat(3, 3));
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::get_geom_rendering
 //       Access: Published
@@ -631,6 +809,29 @@ check_uniform_scale() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::check_uniform_scale2d
+//       Access: Private
+//  Description: Should be called immediately after _scale (and
+//               F_has_components) is set, for a known 2-d scale, this
+//               checks for a identity and/or uniform scale (as well
+//               as a non-zero shear) and sets the bit appropriately.
+////////////////////////////////////////////////////////////////////
+INLINE void TransformState::
+check_uniform_scale2d() {
+  if (IS_NEARLY_EQUAL(_scale[0], _scale[1])) {
+    _scale[2] = _scale[0];
+    _flags |= F_uniform_scale;
+    if (IS_NEARLY_EQUAL(_scale[0], 1.0f)) {
+      _flags |= F_identity_scale;
+    }
+  }
+
+  if (!_shear.almost_equal(LVecBase3f::zero())) {
+    _flags |= F_has_nonzero_shear;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::set_destructing
 //       Access: Private

+ 328 - 65
panda/src/pgraph/transformState.cxx

@@ -55,7 +55,7 @@ TransformState() {
     _states = new States;
   }
   _saved_entry = _states->end();
-  _flags = F_is_identity | F_singular_known;
+  _flags = F_is_identity | F_singular_known | F_is_2d;
   _inv_mat = (LMatrix4f *)NULL;
 }
 
@@ -120,7 +120,7 @@ TransformState::
 bool TransformState::
 operator < (const TransformState &other) const {
   static const int significant_flags = 
-    (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_quat_given);
+    (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_quat_given | F_is_2d);
 
   int flags = (_flags & significant_flags);
   int other_flags = (other._flags & significant_flags);
@@ -185,7 +185,7 @@ get_hash() const {
   size_t hash = 0;
 
   static const int significant_flags = 
-    (F_is_invalid | F_is_identity | F_components_given | F_hpr_given);
+    (F_is_invalid | F_is_identity | F_components_given | F_hpr_given | F_is_2d);
 
   int flags = (_flags & significant_flags);
   hash = int_hash::add_hash(hash, flags);
@@ -327,6 +327,74 @@ make_mat(const LMatrix4f &mat) {
   return return_new(state);
 }
 
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_pos_rotate_scale_shear2d
+//       Access: Published, Static
+//  Description: Makes a new two-dimensional TransformState with the
+//               specified components.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TransformState::
+make_pos_rotate_scale_shear2d(const LVecBase2f &pos, float rotate,
+                              const LVecBase2f &scale,
+                              float shear) {
+  nassertr(!(pos.is_nan() || cnan(rotate) || scale.is_nan() || cnan(shear)) , make_invalid());
+  // Make a special-case check for the identity transform.
+  if (pos == LVecBase2f(0.0f, 0.0f) &&
+      rotate == 0.0f &&
+      scale == LVecBase2f(1.0f, 1.0f) &&
+      shear == 0.0f) {
+    return make_identity();
+  }
+
+  TransformState *state = new TransformState;
+  state->_pos.set(pos[0], pos[1], 0.0f);
+  switch (get_default_coordinate_system()) {
+  default:
+  case CS_zup_right:
+    state->_hpr.set(rotate, 0.0f, 0.0f);
+    break;
+  case CS_zup_left:
+    state->_hpr.set(-rotate, 0.0f, 0.0f);
+    break;
+  case CS_yup_right:
+    state->_hpr.set(0.0f, 0.0f, -rotate);
+    break;
+  case CS_yup_left:
+    state->_hpr.set(0.0, 0.0f, rotate);
+    break;
+  }
+  state->_scale.set(scale[0], scale[1], 1.0f);
+  state->_shear.set(shear, 0.0f, 0.0f);
+  state->_flags = F_components_given | F_hpr_given | F_components_known | F_hpr_known | F_has_components | F_is_2d;
+  state->check_uniform_scale2d();
+  return return_new(state);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::make_mat3
+//       Access: Published, Static
+//  Description: Makes a new two-dimensional TransformState with the
+//               specified 3x3 transformation matrix.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TransformState::
+make_mat3(const LMatrix3f &mat) {
+  nassertr(!mat.is_nan(), make_invalid());
+  // Make a special-case check for the identity matrix.
+  if (mat == LMatrix3f::ident_mat()) {
+    return make_identity();
+  }
+
+  TransformState *state = new TransformState;
+  state->_mat.set(mat(0, 0), mat(0, 1), 0.0f, mat(0, 2),
+                  mat(1, 0), mat(1, 1), 0.0f, mat(1, 2),
+                  0.0f, 0.0f, 1.0f, 0.0f,
+                  mat(2, 0), mat(2, 1), 0.0f, mat(2, 2));
+  state->_flags = F_mat_known | F_is_2d;
+  return return_new(state);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::set_pos
 //       Access: Published
@@ -423,6 +491,102 @@ set_shear(const LVecBase3f &shear) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::set_pos2d
+//       Access: Published
+//  Description: Returns a new TransformState object that represents the
+//               original 2-d TransformState with its pos component
+//               replaced with the indicated value.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TransformState::
+set_pos2d(const LVecBase2f &pos) const {
+  nassertr(!pos.is_nan(), this);
+  nassertr(!is_invalid(), this);
+  if (!is_2d()) {
+    return set_pos(LVecBase3f(pos[0], pos[1], 0.0f));
+  }
+
+  if (is_identity() || components_given()) {
+    // If we started with a componentwise transform, we keep it that
+    // way.
+    return make_pos_rotate_scale_shear2d(pos, get_rotate2d(), get_scale2d(),
+                                         get_shear2d());
+
+  } else {
+    // Otherwise, we have a matrix transform, and we keep it that way.
+    LMatrix3f mat = get_mat3();
+    mat.set_row(2, pos);
+    return make_mat3(mat);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::set_rotate2d
+//       Access: Published
+//  Description: Returns a new TransformState object that represents the
+//               original 2-d TransformState with its rotation component
+//               replaced with the indicated value, if possible.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TransformState::
+set_rotate2d(float rotate) const {
+  nassertr(!cnan(rotate), this);
+  nassertr(!is_invalid(), this);
+
+  if (!is_2d()) {
+    switch (get_default_coordinate_system()) {
+    default:
+    case CS_zup_right:
+      return set_hpr(LVecBase3f(rotate, 0.0f, 0.0f));
+    case CS_zup_left:
+      return set_hpr(LVecBase3f(-rotate, 0.0f, 0.0f));
+    case CS_yup_right:
+      return set_hpr(LVecBase3f(0.0f, 0.0f, -rotate));
+    case CS_yup_left:
+      return set_hpr(LVecBase3f(0.0f, 0.0f, rotate));
+    }
+  }
+
+  return make_pos_rotate_scale_shear2d(get_pos2d(), rotate, get_scale2d(), 
+                                       get_shear2d());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::set_scale2d
+//       Access: Published
+//  Description: Returns a new TransformState object that represents the
+//               original 2-d TransformState with its scale component
+//               replaced with the indicated value, if possible.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TransformState::
+set_scale2d(const LVecBase2f &scale) const {
+  nassertr(!scale.is_nan(), this);
+  nassertr(!is_invalid(), this);
+
+  if (!is_2d()) {
+    return set_scale(LVecBase3f(scale[0], scale[1], 1.0f));
+  }
+  return make_pos_rotate_scale_shear2d(get_pos2d(), get_rotate2d(),
+                                       scale, get_shear2d());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TransformState::set_shear2d
+//       Access: Published
+//  Description: Returns a new TransformState object that represents the
+//               original 2-d TransformState with its shear component
+//               replaced with the indicated value, if possible.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TransformState::
+set_shear2d(float shear) const {
+  nassertr(!cnan(shear), this);
+  nassertr(!is_invalid(), this);
+  if (!is_2d()) {
+    return set_shear(LVecBase3f(shear, 0.0f, 0.0f));
+  }
+  return make_pos_rotate_scale_shear2d(get_pos2d(), get_rotate2d(),
+                                       get_scale2d(), shear);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TransformState::compose
 //       Access: Published
@@ -672,26 +836,50 @@ output(ostream &out) const {
     }
 
     char lead = '(';
-    if (!get_pos().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f))) {
-      out << lead << "pos " << get_pos();
-      lead = ' ';
-    }
-    if (output_hpr) {
-      out << lead << "hpr " << get_hpr();
-      lead = ' ';
-    }
-    if (!get_scale().almost_equal(LVecBase3f(1.0f, 1.0f, 1.0f))) {
-      if (has_uniform_scale()) {
-        out << lead << "scale " << get_uniform_scale();
+    if (is_2d()) {
+      if (!get_pos2d().almost_equal(LVecBase2f(0.0f, 0.0f))) {
+        out << lead << "pos " << get_pos2d();
         lead = ' ';
-      } else {
-        out << lead << "scale " << get_scale();
+      }
+      if (output_hpr) {
+        out << lead << "rotate " << get_rotate2d();
+        lead = ' ';
+      }
+      if (!get_scale2d().almost_equal(LVecBase2f(1.0f, 1.0f))) {
+        if (has_uniform_scale()) {
+          out << lead << "scale " << get_uniform_scale();
+          lead = ' ';
+        } else {
+          out << lead << "scale " << get_scale2d();
+          lead = ' ';
+        }
+      }
+      if (has_nonzero_shear()) {
+        out << lead << "shear " << get_shear2d();
+        lead = ' ';
+      }
+    } else {
+      if (!get_pos().almost_equal(LVecBase3f(0.0f, 0.0f, 0.0f))) {
+        out << lead << "pos " << get_pos();
+        lead = ' ';
+      }
+      if (output_hpr) {
+        out << lead << "hpr " << get_hpr();
+        lead = ' ';
+      }
+      if (!get_scale().almost_equal(LVecBase3f(1.0f, 1.0f, 1.0f))) {
+        if (has_uniform_scale()) {
+          out << lead << "scale " << get_uniform_scale();
+          lead = ' ';
+        } else {
+          out << lead << "scale " << get_scale();
+          lead = ' ';
+        }
+      }
+      if (has_nonzero_shear()) {
+        out << lead << "shear " << get_shear();
         lead = ' ';
       }
-    }
-    if (has_nonzero_shear()) {
-      out << lead << "shear " << get_shear();
-      lead = ' ';
     }
     if (lead == '(') {
       out << "(almost identity)";
@@ -700,7 +888,11 @@ output(ostream &out) const {
     }
 
   } else {
-    out << get_mat();
+    if (is_2d()) {
+      out << get_mat3();
+    } else {
+      out << get_mat();
+    }
   }
 }
 
@@ -1082,18 +1274,36 @@ do_compose(const TransformState *other) const {
     // was given componentwise (and there is no non-uniform scale in
     // the way).
 
-    LVecBase3f pos = get_pos();
-    LQuaternionf quat = get_quat();
-    float scale = get_uniform_scale();
+    CPT(TransformState) result;
+    if (is_2d() && other->is_2d()) {
+      // Do a 2-d compose.
+      LVecBase2f pos = get_pos2d();
+      float rotate = get_rotate2d();
+      LQuaternionf quat = get_quat();
+      float scale = get_uniform_scale();
 
-    pos += quat.xform(other->get_pos()) * scale;
-    quat = other->get_quat() * quat;
-    quat.normalize();
-    LVecBase3f new_scale = other->get_scale() * scale;
+      LPoint3f op = quat.xform(other->get_pos());
+      pos += LVecBase2f(op[0], op[1]) * scale;
 
-    CPT(TransformState) result =
-      make_pos_quat_scale(pos, quat, new_scale);
+      rotate += other->get_rotate2d();
+      LVecBase2f new_scale = other->get_scale2d() * scale;
+      
+      result = make_pos_rotate_scale2d(pos, rotate, new_scale);
 
+    } else {
+      // A normal 3-d compose.
+      LVecBase3f pos = get_pos();
+      LQuaternionf quat = get_quat();
+      float scale = get_uniform_scale();
+      
+      pos += quat.xform(other->get_pos()) * scale;
+      quat = other->get_quat() * quat;
+      quat.normalize();
+      LVecBase3f new_scale = other->get_scale() * scale;
+      
+      result = make_pos_quat_scale(pos, quat, new_scale);
+    }
+      
 #ifndef NDEBUG
     if (paranoid_compose) {
       // Now verify against the matrix.
@@ -1113,8 +1323,13 @@ do_compose(const TransformState *other) const {
   }
 
   // Do the operation with matrices.
-  LMatrix4f new_mat = other->get_mat() * get_mat();
-  return make_mat(new_mat);
+  if (is_2d()) {
+    LMatrix3f new_mat = other->get_mat3() * get_mat3();
+    return make_mat3(new_mat);
+  } else {
+    LMatrix4f new_mat = other->get_mat() * get_mat();
+    return make_mat(new_mat);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1138,30 +1353,63 @@ do_invert_compose(const TransformState *other) const {
     // was given componentwise (and there is no non-uniform scale in
     // the way).
 
-    LVecBase3f pos = get_pos();
-    LQuaternionf quat = get_quat();
-    float scale = get_uniform_scale();
+    CPT(TransformState) result;
+    if (is_2d() && other->is_2d()) {
+      // Do a 2-d invert compose.
+      LVecBase2f pos = get_pos2d();
+      float rotate = get_rotate2d();
+      LQuaternionf quat = get_quat();
+      float scale = get_uniform_scale();
+      
+      // First, invert our own transform.
+      if (scale == 0.0f) {
+        ((TransformState *)this)->_flags |= F_is_singular | F_singular_known;
+        return make_invalid();
+      }
+      scale = 1.0f / scale;
+      quat.invert_in_place();
+      rotate = -rotate;
+      LVecBase3f mp = quat.xform(-LVecBase3f(pos[0], pos[1], 0.0f));
+      pos = LVecBase2f(mp[0], mp[1]) * scale;
+      LVecBase2f new_scale(scale, scale);
+      
+      // Now compose the inverted transform with the other transform.
+      if (!other->is_identity()) {
+        LPoint3f op = quat.xform(other->get_pos());
+        pos += LVecBase2f(op[0], op[1]) * scale;
+
+        rotate += other->get_rotate2d();
+        new_scale = other->get_scale2d() * scale;
+      }
 
-    // First, invert our own transform.
-    if (scale == 0.0f) {
-      ((TransformState *)this)->_flags |= F_is_singular | F_singular_known;
-      return make_invalid();
-    }
-    scale = 1.0f / scale;
-    quat.invert_in_place();
-    pos = quat.xform(-pos) * scale;
-    LVecBase3f new_scale(scale, scale, scale);
+      result = make_pos_rotate_scale2d(pos, rotate, new_scale);
 
-    // Now compose the inverted transform with the other transform.
-    if (!other->is_identity()) {
-      pos += quat.xform(other->get_pos()) * scale;
-      quat = other->get_quat() * quat;
-      quat.normalize();
-      new_scale = other->get_scale() * scale;
-    }
+    } else {
+      // Do a normal, 3-d invert compose.
+      LVecBase3f pos = get_pos();
+      LQuaternionf quat = get_quat();
+      float scale = get_uniform_scale();
+      
+      // First, invert our own transform.
+      if (scale == 0.0f) {
+        ((TransformState *)this)->_flags |= F_is_singular | F_singular_known;
+        return make_invalid();
+      }
+      scale = 1.0f / scale;
+      quat.invert_in_place();
+      pos = quat.xform(-pos) * scale;
+      LVecBase3f new_scale(scale, scale, scale);
+      
+      // Now compose the inverted transform with the other transform.
+      if (!other->is_identity()) {
+        pos += quat.xform(other->get_pos()) * scale;
+        quat = other->get_quat() * quat;
+        quat.normalize();
+        new_scale = other->get_scale() * scale;
+      }
 
-    CPT(TransformState) result =
-      make_pos_quat_scale(pos, quat, new_scale);
+      result = make_pos_quat_scale(pos, quat, new_scale);
+    }
 
 #ifndef NDEBUG
     if (paranoid_compose) {
@@ -1195,10 +1443,22 @@ do_invert_compose(const TransformState *other) const {
   // _inv_mat has been allocated and filled in.
   nassertr(_inv_mat != (LMatrix4f *)NULL, make_invalid());
 
-  if (other->is_identity()) {
-    return make_mat(*_inv_mat);
+  if (is_2d()) {
+    const LMatrix4f &i = *_inv_mat;
+    LMatrix3f inv3(i(0, 0), i(0, 1), i(0, 3),
+                   i(1, 0), i(1, 1), i(1, 3),
+                   i(3, 0), i(3, 1), i(3, 3));
+    if (other->is_identity()) {
+      return make_mat3(inv3);
+    } else {
+      return make_mat3(other->get_mat3() * inv3);
+    }
   } else {
-    return make_mat(other->get_mat() * (*_inv_mat));
+    if (other->is_identity()) {
+      return make_mat(*_inv_mat);
+    } else {
+      return make_mat(other->get_mat() * (*_inv_mat));
+    }
   }
 }
 
@@ -1547,24 +1807,25 @@ write_datagram(BamWriter *manager, Datagram &dg) {
 
   if ((_flags & F_is_identity) != 0) {
     // Identity, nothing much to that.
-    int flags = F_is_identity | F_singular_known;
-    dg.add_uint16(flags);
+    int flags = F_is_identity | F_singular_known | F_is_2d;
+    dg.add_uint32(flags);
 
   } else if ((_flags & F_is_invalid) != 0) {
     // Invalid, nothing much to that either.
     int flags = F_is_invalid | F_singular_known | F_is_singular | F_components_known | F_mat_known;
-    dg.add_uint16(flags);
+    dg.add_uint32(flags);
 
   } else if ((_flags & F_components_given) != 0) {
     // A component-based transform.
     int flags = F_components_given | F_components_known | F_has_components;
+    flags |= (_flags & F_is_2d);
     if ((_flags & F_quat_given) != 0) {
       flags |= (F_quat_given | F_quat_known);
     } else if ((_flags & F_hpr_given) != 0) {
       flags |= (F_hpr_given | F_hpr_known);
     }
 
-    dg.add_uint16(flags);
+    dg.add_uint32(flags);
 
     _pos.write_datagram(dg);
     if ((_flags & F_quat_given) != 0) {
@@ -1579,7 +1840,8 @@ write_datagram(BamWriter *manager, Datagram &dg) {
     // A general matrix.
     nassertv((_flags & F_mat_known) != 0);
     int flags = F_mat_known;
-    dg.add_uint16(flags);
+    flags |= (_flags & F_is_2d);
+    dg.add_uint32(flags);
     _mat.write_datagram(dg);
   }
 }
@@ -1668,7 +1930,11 @@ void TransformState::
 fillin(DatagramIterator &scan, BamReader *manager) {
   TypedWritable::fillin(scan, manager);
 
-  _flags = scan.get_uint16();
+  if (manager->get_file_minor_ver() < 2) {
+    _flags = scan.get_uint16();
+  } else {
+    _flags = scan.get_uint32();
+  }
 
   if ((_flags & F_components_given) != 0) {
     // Componentwise transform.
@@ -1677,9 +1943,6 @@ fillin(DatagramIterator &scan, BamReader *manager) {
       _quat.read_datagram(scan);
     } else {
       _hpr.read_datagram(scan);
-      // Holdover support for bams 4.0 or older: add these bits that
-      // should have been added when the bam was written.
-      _flags |= (F_hpr_given | F_hpr_known);
     }
     _scale.read_datagram(scan);
     _shear.read_datagram(scan);

+ 52 - 18
panda/src/pgraph/transformState.h

@@ -79,7 +79,7 @@ PUBLISHED:
                                                  const LVecBase3f &hpr);
   INLINE static CPT(TransformState) make_scale(float scale);
   INLINE static CPT(TransformState) make_scale(const LVecBase3f &scale);
-  INLINE static CPT(TransformState) make_shear(const LVecBase3f &scale);
+  INLINE static CPT(TransformState) make_shear(const LVecBase3f &shear);
   INLINE static CPT(TransformState) make_pos_hpr_scale(const LVecBase3f &pos,
                                                        const LVecBase3f &hpr, 
                                                        const LVecBase3f &scale);
@@ -96,9 +96,29 @@ PUBLISHED:
                                                        const LVecBase3f &shear);
   static CPT(TransformState) make_mat(const LMatrix4f &mat);
 
+
+  INLINE static CPT(TransformState) make_pos2d(const LVecBase2f &pos);
+  INLINE static CPT(TransformState) make_rotate2d(float rotate);
+  INLINE static CPT(TransformState) make_pos_rotate2d(const LVecBase2f &pos,
+                                                      float rotate);
+  INLINE static CPT(TransformState) make_scale2d(float scale);
+  INLINE static CPT(TransformState) make_scale2d(const LVecBase2f &scale);
+  INLINE static CPT(TransformState) make_shear2d(float shear);
+  INLINE static CPT(TransformState) make_pos_rotate_scale2d(const LVecBase2f &pos,
+                                                            float rotate, 
+                                                            const LVecBase2f &scale);
+  static CPT(TransformState) make_pos_rotate_scale_shear2d(const LVecBase2f &pos,
+                                                           float rotate,
+                                                           const LVecBase2f &scale,
+                                                           float shear);
+  static CPT(TransformState) make_mat3(const LMatrix3f &mat);
+
+
   INLINE bool is_identity() const;
   INLINE bool is_invalid() const;
   INLINE bool is_singular() const;
+  INLINE bool is_2d() const;
+
   INLINE bool has_components() const;
   INLINE bool components_given() const;
   INLINE bool hpr_given() const;
@@ -112,6 +132,7 @@ PUBLISHED:
   INLINE bool has_shear() const;
   INLINE bool has_nonzero_shear() const;
   INLINE bool has_mat() const;
+
   INLINE const LVecBase3f &get_pos() const;
   INLINE const LVecBase3f &get_hpr() const;
   INLINE const LQuaternionf &get_quat() const;
@@ -120,12 +141,23 @@ PUBLISHED:
   INLINE const LVecBase3f &get_shear() const;
   INLINE const LMatrix4f &get_mat() const;
 
+  INLINE LVecBase2f get_pos2d() const;
+  INLINE float get_rotate2d() const;
+  INLINE LVecBase2f get_scale2d() const;
+  INLINE float get_shear2d() const;
+  INLINE LMatrix3f get_mat3() const;
+
   CPT(TransformState) set_pos(const LVecBase3f &pos) const;
   CPT(TransformState) set_hpr(const LVecBase3f &hpr) const;
   CPT(TransformState) set_quat(const LQuaternionf &quat) const;
   CPT(TransformState) set_scale(const LVecBase3f &scale) const;
   CPT(TransformState) set_shear(const LVecBase3f &shear) const;
 
+  CPT(TransformState) set_pos2d(const LVecBase2f &pos) const;
+  CPT(TransformState) set_rotate2d(float rotate) const;
+  CPT(TransformState) set_scale2d(const LVecBase2f &scale) const;
+  CPT(TransformState) set_shear2d(float shear) const;
+
   CPT(TransformState) compose(const TransformState *other) const;
   CPT(TransformState) invert_compose(const TransformState *other) const;
 
@@ -229,6 +261,7 @@ private:
   void calc_mat();
 
   INLINE void check_uniform_scale();
+  INLINE void check_uniform_scale2d();
 
   INLINE void set_destructing();
   INLINE bool is_destructing() const;
@@ -237,29 +270,30 @@ private:
   static void update_pstats(int old_referenced_bits, int new_referenced_bits);
 
   enum Flags {
-    F_is_identity        = 0x0001,
-    F_is_singular        = 0x0002,
-    F_singular_known     = 0x0004,  // set if we know F_is_singular
-    F_components_given   = 0x0008,
-    F_components_known   = 0x0010,  // set if we know F_has_components
-    F_has_components     = 0x0020,
-    F_mat_known          = 0x0040,  // set if _mat is defined
-    F_is_invalid         = 0x0080,
-    F_quat_given         = 0x0100,
-    F_quat_known         = 0x0200,  // set if _quat is defined
-    F_hpr_given          = 0x0400,
-    F_hpr_known          = 0x0800,  // set if _hpr is defined
-    F_uniform_scale      = 0x1000,
-    F_identity_scale     = 0x2000,
-    F_has_nonzero_shear  = 0x4000,
-    F_is_destructing     = 0x8000,
+    F_is_identity        = 0x00000001,
+    F_is_singular        = 0x00000002,
+    F_singular_known     = 0x00000004,  // set if we know F_is_singular
+    F_components_given   = 0x00000008,
+    F_components_known   = 0x00000010,  // set if we know F_has_components
+    F_has_components     = 0x00000020,
+    F_mat_known          = 0x00000040,  // set if _mat is defined
+    F_is_invalid         = 0x00000080,
+    F_quat_given         = 0x00000100,
+    F_quat_known         = 0x00000200,  // set if _quat is defined
+    F_hpr_given          = 0x00000400,
+    F_hpr_known          = 0x00000800,  // set if _hpr is defined
+    F_uniform_scale      = 0x00001000,
+    F_identity_scale     = 0x00002000,
+    F_has_nonzero_shear  = 0x00004000,
+    F_is_destructing     = 0x00008000,
+    F_is_2d              = 0x00010000,
   };
   LVecBase3f _pos, _hpr, _scale, _shear;
   LQuaternionf _quat;
   LMatrix4f _mat;
   LMatrix4f *_inv_mat;
   
-  unsigned short _flags;
+  unsigned int _flags;
 
 public:
   static void register_with_read_factory();

+ 2 - 1
panda/src/putil/bam.h

@@ -35,8 +35,9 @@ static const unsigned short _bam_major_ver = 5;
 // Bumped to major version 4 on 4/10/02 to store new scene graph.
 // Bumped to major version 5 on 5/6/05 for new Geom implementation.
 
-static const unsigned short _bam_minor_ver = 1;
+static const unsigned short _bam_minor_ver = 2;
 // Bumped to minor version 1 on 7/14/05 to add TextureStage::_saved_result.
+// Bumped to minor version 2 on 7/21/05 to add TransformState::is_2d.
 
 
 #endif