Browse Source

compute and load tangent and binormal

David Rose 20 years ago
parent
commit
5d83609638

+ 21 - 0
panda/src/egg/eggGroupNode.I

@@ -17,3 +17,24 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroupNode::TBNVertexValue::operator <
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool EggGroupNode::TBNVertexValue::
+operator < (const TBNVertexValue &other) const {
+  int compare = _pos.compare_to(other._pos);
+  if (compare != 0) {
+    return compare < 0;
+  }
+  compare = _normal.compare_to(other._normal);
+  if (compare != 0) {
+    return compare < 0;
+  }
+  compare = _uv.compare_to(other._uv);
+  if (compare != 0) {
+    return compare < 0;
+  }
+  return _uv_name < other._uv_name;
+}

+ 196 - 1
panda/src/egg/eggGroupNode.cxx

@@ -695,6 +695,45 @@ strip_normals() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroupNode::recompute_tangent_binormal
+//       Access: Published
+//  Description: This function recomputes the tangent and binormal for
+//               the named texture coordinate set for all vertices at
+//               this level and below.  Use the empty string for the
+//               default texture coordinate set.
+//
+//               It is necessary for each vertex to already have a
+//               normal (or at least a polygon normal), as well as a
+//               texture coordinate in the named texture coordinate
+//               set, before calling this function.  You might precede
+//               this with recompute_vertex_normals() to ensure that
+//               the normals exist.
+//
+//               Like recompute_vertex_normals(), this function does
+//               not remove or adjust vertices in the vertex pool; it
+//               only adds new vertices with the normals removed.
+//               Thus, it is a good idea to call
+//               remove_unused_vertices() after calling this.
+////////////////////////////////////////////////////////////////////
+void EggGroupNode::
+recompute_tangent_binormal(const GlobPattern &uv_name) {
+  // First, collect all the vertices together with their shared
+  // polygons.
+  TBNVertexCollection collection;
+  r_collect_tangent_binormal(uv_name, collection);
+
+  // Now compute the tangent and binormal separately for each common
+  // group of vertices.
+  TBNVertexCollection::const_iterator ci;
+  for (ci = collection.begin(); ci != collection.end(); ++ci) {
+    const TBNVertexValue &value = (*ci).first;
+    const TBNVertexGroup &group = (*ci).second;
+
+    do_compute_tangent_binormal(value, group);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggGroupNode::triangulate_polygons
 //       Access: Published
@@ -1644,7 +1683,7 @@ r_collect_vertex_normals(EggGroupNode::NVertexCollection &collection,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: EggGroupNode::r_collect_vertex_normals
+//     Function: EggGroupNode::do_compute_vertex_normals
 //       Access: Private
 //  Description: This is part of the implementation of
 //               recompute_vertex_normals().  It accepts a group of
@@ -1683,3 +1722,159 @@ do_compute_vertex_normals(const NVertexGroup &group) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroupNode::r_collect_tangent_binormal
+//       Access: Private
+//  Description: This is part of the implementation of
+//               recompute_tangent_binormal().  It walks the scene
+//               graph at this group node and below, identifying all
+//               the polygons and the vertices they have in common.
+////////////////////////////////////////////////////////////////////
+void EggGroupNode::
+r_collect_tangent_binormal(const GlobPattern &uv_name,
+                           EggGroupNode::TBNVertexCollection &collection) {
+  Children::iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    EggNode *child = *ci;
+
+    if (child->is_of_type(EggPolygon::get_class_type())) {
+      EggPolygon *polygon = DCAST(EggPolygon, child);
+
+      TBNVertexReference ref;
+      ref._polygon = polygon;
+
+      // Now add each vertex from the polygon separately to our
+      // collection.
+      size_t num_vertices = polygon->size();
+      for (size_t i = 0; i < num_vertices; i++) {
+        // We look at the triangle formed by each three consecutive
+        // vertices to determine the s direction and t direction at
+        // each vertex.  v1 is the key vertex, the one at position i;
+        // v2 is vertex i + 1, and v3 is vertex i - 1.
+        EggVertex *v1 = polygon->get_vertex(i);
+        EggVertex *v2 = polygon->get_vertex((i + 1) % num_vertices);
+        EggVertex *v3 = polygon->get_vertex((i + num_vertices - 1) % num_vertices);
+        if (v1->has_normal() || polygon->has_normal()) {
+          // Go through all of the UV names on the vertex, looking for
+          // one that matches the glob pattern.
+          EggVertex::const_uv_iterator uvi;
+          for (uvi = v1->uv_begin(); uvi != v1->uv_end(); ++uvi) {
+            EggVertexUV *uv_obj = (*uvi);
+            string name = uv_obj->get_name();
+            if (uv_name.matches(name) &&
+                v2->has_uv(name) && v3->has_uv(name)) {
+              TBNVertexValue value;
+              value._uv_name = name;
+              value._pos = v1->get_pos3();
+              if (v1->has_normal()) {
+                value._normal = v1->get_normal();
+              } else {
+                value._normal = polygon->get_normal();
+              }
+              value._uv = v1->get_uv(name);
+              
+              // Compute the s direction and t direction for this vertex.
+              LPoint3d p1 = v1->get_pos3();
+              LPoint3d p2 = v2->get_pos3();
+              LPoint3d p3 = v3->get_pos3();
+              
+              TexCoordd w1 = v1->get_uv(name);
+              TexCoordd w2 = v2->get_uv(name);
+              TexCoordd w3 = v3->get_uv(name);
+              
+              double x1 = p2[0] - p1[0];
+              double x2 = p3[0] - p1[0];
+              double y1 = p2[1] - p1[1];
+              double y2 = p3[1] - p1[1];
+              double z1 = p2[2] - p1[2];
+              double z2 = p3[2] - p1[2];
+              
+              double s1 = w2[0] - w1[0];
+              double s2 = w3[0] - w1[0];
+              double t1 = w2[1] - w1[1];
+              double t2 = w3[1] - w1[1];
+              
+              double r = 1.0f / (s1 * t2 - s2 * t1);
+              ref._sdir.set((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
+                            (t2 * z1 - t1 * z2) * r);
+              ref._tdir.set((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
+                            (s1 * z2 - s2 * z1) * r);
+
+              // Store the vertex referenced to the polygon.
+              ref._vertex = i;
+              collection[value].push_back(ref);
+            }
+          }
+        }
+      }
+
+    } else if (child->is_of_type(EggGroupNode::get_class_type())) {
+      EggGroupNode *group = DCAST(EggGroupNode, child);
+
+      // We can't share vertices across an Instance node.  Don't
+      // even bother trying.  Instead, just restart.
+      if (group->is_under_instance()) {
+        group->recompute_tangent_binormal(uv_name);
+      } else {
+        group->r_collect_tangent_binormal(uv_name, collection);
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggGroupNode::do_compute_tangent_binormal
+//       Access: Private
+//  Description: This is part of the implementation of
+//               recompute_tangent_binormal().  It accepts a group of
+//               polygons and their common normals and UV's, and
+//               computes the tangent and binormal for all their
+//               shared vertices.
+////////////////////////////////////////////////////////////////////
+void EggGroupNode::
+do_compute_tangent_binormal(const TBNVertexValue &value,
+                            const TBNVertexGroup &group) {
+  nassertv(!group.empty());
+
+  // Accumulate together all of the s vectors and t vectors computed
+  // for the different vertices that are together here.
+  Normald sdir(0.0, 0.0, 0.0);
+  Normald tdir(0.0, 0.0, 0.0);
+
+  TBNVertexGroup::const_iterator gi;
+  for (gi = group.begin(); gi != group.end(); ++gi) {
+    const TBNVertexReference &ref = (*gi);
+    sdir += ref._sdir;
+    tdir += ref._tdir;
+  }
+
+  Normald tangent = (sdir - value._normal * value._normal.dot(sdir));
+  tangent.normalize();
+
+  Normald binormal = cross(value._normal, tangent);
+  if (dot(binormal, tdir) < 0.0f) {
+    binormal = -binormal;
+  }
+  // Shouldn't need to normalize this, but we do just for good measure.
+  binormal.normalize();
+
+  // Now we have the common tangent and binormal; apply them to all
+  // the vertices.
+
+  for (gi = group.begin(); gi != group.end(); ++gi) {
+    const TBNVertexReference &ref = (*gi);
+    EggVertex *vertex = ref._polygon->get_vertex(ref._vertex);
+    EggVertexPool *pool = vertex->get_pool();
+
+    EggVertex new_vertex(*vertex);
+    EggVertexUV *uv_obj = new_vertex.get_uv_obj(value._uv_name);
+    nassertv(uv_obj != (EggVertexUV *)NULL);
+    uv_obj->set_tangent(tangent);
+    uv_obj->set_binormal(binormal);
+
+    EggVertex *unique = pool->create_unique_vertex(new_vertex);
+    unique->copy_grefs_from(*vertex);
+
+    ref._polygon->set_vertex(ref._vertex, unique);
+  }
+}

+ 27 - 1
panda/src/egg/eggGroupNode.h

@@ -27,7 +27,7 @@
 #include "typedObject.h"
 #include "pointerTo.h"
 #include "luse.h"
-
+#include "globPattern.h"
 #include "plist.h"
 
 class EggTextureCollection;
@@ -130,6 +130,8 @@ PUBLISHED:
   void recompute_polygon_normals(CoordinateSystem cs = CS_default);
   void strip_normals();
 
+  void recompute_tangent_binormal(const GlobPattern &uv_name);
+
   enum TriangulateFlags {
     T_polygon     = 0x001,
     T_convex      = 0x002,
@@ -197,6 +199,30 @@ private:
                                 double threshold, CoordinateSystem cs);
   void do_compute_vertex_normals(const NVertexGroup &group);
 
+  // This bit is in support of recompute_tangent_binormal().
+  class TBNVertexReference {
+  public:
+    EggPolygon *_polygon;
+    size_t _vertex;
+    LVector3d _sdir;
+    LVector3d _tdir;
+  };
+  class TBNVertexValue {
+  public:
+    INLINE bool operator < (const TBNVertexValue &other) const;
+    Vertexd _pos;
+    Normald _normal;
+    string _uv_name;
+    TexCoordd _uv;
+  };
+  typedef pvector<TBNVertexReference> TBNVertexGroup;
+  typedef pmap<TBNVertexValue, TBNVertexGroup> TBNVertexCollection;
+
+  void r_collect_tangent_binormal(const GlobPattern &uv_name,
+                                  TBNVertexCollection &collection);
+  void do_compute_tangent_binormal(const TBNVertexValue &value,
+                                   const TBNVertexGroup &group);
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 5 - 0
panda/src/egg/eggVertex.cxx

@@ -412,6 +412,11 @@ transform(const LMatrix4d &mat) {
     morph.set_offset((*mi).get_offset() * mat);
   }
 
+  UVMap::iterator ui;
+  for (ui = _uv_map.begin(); ui != _uv_map.end(); ++ui) {
+    (*ui).second->transform(mat);
+  }
+
   EggAttributes::transform(mat);
 }
 

+ 15 - 7
panda/src/egg/eggVertexPool.cxx

@@ -295,26 +295,34 @@ has_uvs() const {
 //     Function: EggVertexPool::get_uv_names
 //       Access: Public
 //  Description: Returns the list of UV names that are defined by any
-//               vertices in the pool.  It is the user's
-//               responsibility to clear the vector before calling
-//               this method.
+//               vertices in the pool.  Also returns the subset of UV
+//               names that define the tangent and binormal.  It is
+//               the user's responsibility to clear both vectors
+//               before calling this method.
 ////////////////////////////////////////////////////////////////////
 void EggVertexPool::
-get_uv_names(vector_string &uv_names) const {
-  pset<string> names;
+get_uv_names(vector_string &uv_names, vector_string &tbn_names) const {
+  pset<string> uv_names_set, tbn_names_set;
   IndexVertices::const_iterator ivi;
   for (ivi = _index_vertices.begin(); ivi != _index_vertices.end(); ++ivi) {
     EggVertex *vertex = (*ivi).second;
     EggVertex::const_uv_iterator uvi;
     for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
-      names.insert((*uvi)->get_name());
+      EggVertexUV *uv_obj = (*uvi);
+      uv_names_set.insert(uv_obj->get_name());
+      if (uv_obj->has_tangent() && uv_obj->has_binormal()) {
+        tbn_names_set.insert(uv_obj->get_name());
+      }
     }
   }
 
   pset<string>::const_iterator si;
-  for (si = names.begin(); si != names.end(); ++si) {
+  for (si = uv_names_set.begin(); si != uv_names_set.end(); ++si) {
     uv_names.push_back(*si);
   }
+  for (si = tbn_names_set.begin(); si != tbn_names_set.end(); ++si) {
+    tbn_names.push_back(*si);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 1
panda/src/egg/eggVertexPool.h

@@ -98,7 +98,7 @@ PUBLISHED:
   bool has_colors() const;
   bool has_nonwhite_colors() const;
   bool has_uvs() const;
-  void get_uv_names(vector_string &uv_names) const;
+  void get_uv_names(vector_string &uv_names, vector_string &tbn_names) const;
 
 public:
   // Can be used to traverse all the vertices in index number order.

+ 84 - 0
panda/src/egg/eggVertexUV.I

@@ -36,3 +36,87 @@ INLINE void EggVertexUV::
 set_uv(const TexCoordd &uv) {
   _uv = uv;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::has_tangent
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool EggVertexUV::
+has_tangent() const {
+  return (_flags & F_has_tangent) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::get_tangent
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const Normald &EggVertexUV::
+get_tangent() const {
+  nassertr(has_tangent(), _tangent);
+  return _tangent;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::set_tangent
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggVertexUV::
+set_tangent(const Normald &tangent) {
+  _tangent = tangent;
+  _flags |= F_has_tangent;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::clear_tangent
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggVertexUV::
+clear_tangent() {
+  _flags &= ~F_has_tangent;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::has_binormal
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool EggVertexUV::
+has_binormal() const {
+  return (_flags & F_has_binormal) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::get_binormal
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE const Normald &EggVertexUV::
+get_binormal() const {
+  nassertr(has_binormal(), _binormal);
+  return _binormal;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::set_binormal
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggVertexUV::
+set_binormal(const Normald &binormal) {
+  _binormal = binormal;
+  _flags |= F_has_binormal;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::clear_binormal
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggVertexUV::
+clear_binormal() {
+  _flags &= ~F_has_binormal;
+}

+ 55 - 3
panda/src/egg/eggVertexUV.cxx

@@ -31,6 +31,7 @@ TypeHandle EggVertexUV::_type_handle;
 EggVertexUV::
 EggVertexUV(const string &name, const TexCoordd &uv) :
   EggNamedObject(name),
+  _flags(0),
   _uv(uv)
 {
 }
@@ -44,6 +45,9 @@ EggVertexUV::
 EggVertexUV(const EggVertexUV &copy) :
   EggNamedObject(copy),
   _duvs(copy._duvs),
+  _flags(copy._flags),
+  _tangent(copy._tangent),
+  _binormal(copy._binormal),
   _uv(copy._uv)
 {
 }
@@ -57,6 +61,9 @@ EggVertexUV &EggVertexUV::
 operator = (const EggVertexUV &copy) {
   EggNamedObject::operator = (copy);
   _duvs = copy._duvs;
+  _flags = copy._flags;
+  _tangent = copy._tangent;
+  _binormal = copy._binormal;
   _uv = copy._uv;
 
   return (*this);
@@ -71,6 +78,25 @@ EggVertexUV::
 ~EggVertexUV() {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggVertexUV::transform
+//       Access: Published, Virtual
+//  Description: Applies the indicated transformation matrix to the
+//               UV's tangent and/or binormal.  This does nothing if
+//               there is no tangent or binormal.
+////////////////////////////////////////////////////////////////////
+void EggVertexUV::
+transform(const LMatrix4d &mat) {
+  if (has_tangent()) {
+    _tangent = _tangent * mat;
+    _tangent.normalize();
+  }
+  if (has_binormal()) {
+    _binormal = _binormal * mat;
+    _binormal.normalize();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggVertexUV::write
 //       Access: Public
@@ -83,12 +109,20 @@ write(ostream &out, int indent_level) const {
     inline_name += ' ';
   }
 
-  if (_duvs.empty()) {
+  if (_duvs.empty() && _flags == 0) {
     indent(out, indent_level)
       << "<UV> " << inline_name << "{ " << get_uv() << " }\n";
   } else {
     indent(out, indent_level) << "<UV> " << inline_name << "{\n";
     indent(out, indent_level+2) << get_uv() << "\n";
+    if (has_tangent()) {
+      indent(out, indent_level + 2)
+        << "<Tangent> { " << get_tangent() << " }\n";
+    }
+    if (has_binormal()) {
+      indent(out, indent_level + 2)
+        << "<Binormal> { " << get_binormal() << " }\n";
+    }
     _duvs.write(out, indent_level+2);
     indent(out, indent_level) << "}\n";
   }
@@ -103,11 +137,29 @@ write(ostream &out, int indent_level) const {
 ////////////////////////////////////////////////////////////////////
 int EggVertexUV::
 compare_to(const EggVertexUV &other) const {
-  int compare =
-    _uv.compare_to(other._uv, egg_parameters->_uv_threshold);
+  if (_flags != other._flags) {
+    return _flags - other._flags;
+  }
+  int compare;
+  compare = _uv.compare_to(other._uv, egg_parameters->_uv_threshold);
   if (compare != 0) {
     return compare;
   }
+
+  if (has_tangent()) {
+    compare = _tangent.compare_to(other._tangent, egg_parameters->_normal_threshold);
+    if (compare != 0) {
+      return compare;
+    }
+  }
+
+  if (has_binormal()) {
+    compare = _binormal.compare_to(other._binormal, egg_parameters->_normal_threshold);
+    if (compare != 0) {
+      return compare;
+    }
+  }
+
   if (_duvs != other._duvs) {
     return _duvs < other._duvs ? -1 : 1;
   }

+ 20 - 0
panda/src/egg/eggVertexUV.h

@@ -43,12 +43,32 @@ PUBLISHED:
   INLINE const TexCoordd &get_uv() const;
   INLINE void set_uv(const TexCoordd &texCoord);
 
+  INLINE bool has_tangent() const;
+  INLINE const Normald &get_tangent() const;
+  INLINE void set_tangent(const Normald &tangent);
+  INLINE void clear_tangent();
+
+  INLINE bool has_binormal() const;
+  INLINE const Normald &get_binormal() const;
+  INLINE void set_binormal(const Normald &binormal);
+  INLINE void clear_binormal();
+
+  void transform(const LMatrix4d &mat);
+
   void write(ostream &out, int indent_level) const;
   int compare_to(const EggVertexUV &other) const;
 
   EggMorphTexCoordList _duvs;
 
 private:
+  enum Flags {
+    F_has_tangent   = 0x001,
+    F_has_binormal  = 0x002,
+  };
+
+  int _flags;
+  Normald _tangent;
+  Normald _binormal;
   TexCoordd _uv;
 
 public:

File diff suppressed because it is too large
+ 371 - 364
panda/src/egg/lexer.cxx.prebuilt


+ 8 - 0
panda/src/egg/lexer.lxx

@@ -351,6 +351,10 @@ NUMERIC         ([+-]?(([0-9]+[.]?)|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   accept();
   return BILLBOARDCENTER;
 }
+"<BINORMAL>" {
+  accept();
+  return BINORMAL;
+}
 "<BUNDLE>" {
   accept();
   return BUNDLE;
@@ -579,6 +583,10 @@ NUMERIC         ([+-]?(([0-9]+[.]?)|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
   accept();
   return TAG;
 }
+"<TANGENT>" {
+  accept();
+  return TANGENT;
+}
 "<TEXLIST>" {
   accept();
   return TEXLIST;

File diff suppressed because it is too large
+ 709 - 696
panda/src/egg/parser.cxx.prebuilt


+ 81 - 79
panda/src/egg/parser.h.prebuilt

@@ -8,85 +8,87 @@
 # define	BFACE	261
 # define	BILLBOARD	262
 # define	BILLBOARDCENTER	263
-# define	BUNDLE	264
-# define	CLOSED	265
-# define	COLLIDE	266
-# define	COMMENT	267
-# define	COMPONENT	268
-# define	COORDSYSTEM	269
-# define	CV	270
-# define	DART	271
-# define	DNORMAL	272
-# define	DRGBA	273
-# define	DUV	274
-# define	DXYZ	275
-# define	DCS	276
-# define	DISTANCE	277
-# define	DTREF	278
-# define	DYNAMICVERTEXPOOL	279
-# define	EXTERNAL_FILE	280
-# define	FLIGHT	281
-# define	GROUP	282
-# define	HIP	283
-# define	INTANGENT	284
-# define	JOINT	285
-# define	KNOTS	286
-# define	INCLUDE	287
-# define	INSTANCE	288
-# define	LINE	289
-# define	LOOP	290
-# define	MATERIAL	291
-# define	MATRIX3	292
-# define	MATRIX4	293
-# define	MODEL	294
-# define	MREF	295
-# define	NORMAL	296
-# define	NURBSCURVE	297
-# define	NURBSSURFACE	298
-# define	OBJECTTYPE	299
-# define	ORDER	300
-# define	OUTTANGENT	301
-# define	POINTLIGHT	302
-# define	POLYGON	303
-# define	REF	304
-# define	RGBA	305
-# define	ROTATE	306
-# define	ROTX	307
-# define	ROTY	308
-# define	ROTZ	309
-# define	SANIM	310
-# define	SCALAR	311
-# define	SCALE	312
-# define	SEQUENCE	313
-# define	SHADING	314
-# define	SWITCH	315
-# define	SWITCHCONDITION	316
-# define	TABLE	317
-# define	TABLE_V	318
-# define	TAG	319
-# define	TEXLIST	320
-# define	TEXTURE	321
-# define	TLENGTHS	322
-# define	TRANSFORM	323
-# define	TRANSLATE	324
-# define	TREF	325
-# define	TRIANGLEFAN	326
-# define	TRIANGLESTRIP	327
-# define	TRIM	328
-# define	TXT	329
-# define	UKNOTS	330
-# define	UV	331
-# define	VKNOTS	332
-# define	VERTEX	333
-# define	VERTEXANIM	334
-# define	VERTEXPOOL	335
-# define	VERTEXREF	336
-# define	XFMANIM	337
-# define	XFMSANIM	338
-# define	START_EGG	339
-# define	START_GROUP_BODY	340
-# define	START_TEXTURE_BODY	341
-# define	START_PRIMITIVE_BODY	342
+# define	BINORMAL	264
+# define	BUNDLE	265
+# define	CLOSED	266
+# define	COLLIDE	267
+# define	COMMENT	268
+# define	COMPONENT	269
+# define	COORDSYSTEM	270
+# define	CV	271
+# define	DART	272
+# define	DNORMAL	273
+# define	DRGBA	274
+# define	DUV	275
+# define	DXYZ	276
+# define	DCS	277
+# define	DISTANCE	278
+# define	DTREF	279
+# define	DYNAMICVERTEXPOOL	280
+# define	EXTERNAL_FILE	281
+# define	FLIGHT	282
+# define	GROUP	283
+# define	HIP	284
+# define	INTANGENT	285
+# define	JOINT	286
+# define	KNOTS	287
+# define	INCLUDE	288
+# define	INSTANCE	289
+# define	LINE	290
+# define	LOOP	291
+# define	MATERIAL	292
+# define	MATRIX3	293
+# define	MATRIX4	294
+# define	MODEL	295
+# define	MREF	296
+# define	NORMAL	297
+# define	NURBSCURVE	298
+# define	NURBSSURFACE	299
+# define	OBJECTTYPE	300
+# define	ORDER	301
+# define	OUTTANGENT	302
+# define	POINTLIGHT	303
+# define	POLYGON	304
+# define	REF	305
+# define	RGBA	306
+# define	ROTATE	307
+# define	ROTX	308
+# define	ROTY	309
+# define	ROTZ	310
+# define	SANIM	311
+# define	SCALAR	312
+# define	SCALE	313
+# define	SEQUENCE	314
+# define	SHADING	315
+# define	SWITCH	316
+# define	SWITCHCONDITION	317
+# define	TABLE	318
+# define	TABLE_V	319
+# define	TAG	320
+# define	TANGENT	321
+# define	TEXLIST	322
+# define	TEXTURE	323
+# define	TLENGTHS	324
+# define	TRANSFORM	325
+# define	TRANSLATE	326
+# define	TREF	327
+# define	TRIANGLEFAN	328
+# define	TRIANGLESTRIP	329
+# define	TRIM	330
+# define	TXT	331
+# define	UKNOTS	332
+# define	UV	333
+# define	VKNOTS	334
+# define	VERTEX	335
+# define	VERTEXANIM	336
+# define	VERTEXPOOL	337
+# define	VERTEXREF	338
+# define	XFMANIM	339
+# define	XFMSANIM	340
+# define	START_EGG	341
+# define	START_GROUP_BODY	342
+# define	START_TEXTURE_BODY	343
+# define	START_PRIMITIVE_BODY	344
 
 
 extern YYSTYPE eggyylval;

+ 18 - 2
panda/src/egg/parser.yxx

@@ -146,7 +146,7 @@ egg_cleanup_parser() {
 %token <_ulong> ULONG
 %token <_string> STRING
 
-%token BEZIERCURVE BFACE BILLBOARD BILLBOARDCENTER BUNDLE CLOSED
+%token BEZIERCURVE BFACE BILLBOARD BILLBOARDCENTER BINORMAL BUNDLE CLOSED
 %token COLLIDE COMMENT COMPONENT
 %token COORDSYSTEM CV DART
 %token DNORMAL DRGBA DUV DXYZ DCS DISTANCE DTREF
@@ -157,7 +157,7 @@ egg_cleanup_parser() {
 %token NURBSCURVE NURBSSURFACE OBJECTTYPE ORDER
 %token OUTTANGENT POINTLIGHT POLYGON REF RGBA ROTATE ROTX ROTY ROTZ
 %token SANIM SCALAR SCALE SEQUENCE SHADING SWITCH SWITCHCONDITION
-%token TABLE TABLE_V TAG TEXLIST TEXTURE TLENGTHS TRANSFORM TRANSLATE
+%token TABLE TABLE_V TAG TANGENT TEXLIST TEXTURE TLENGTHS TRANSFORM TRANSLATE
 %token TREF TRIANGLEFAN TRIANGLESTRIP 
 %token TRIM TXT UKNOTS UV VKNOTS VERTEX VERTEXANIM
 %token VERTEXPOOL VERTEXREF
@@ -952,6 +952,22 @@ vertex_uv_body:
         real real
 {
   DCAST(EggVertexUV, egg_stack.back())->set_uv(TexCoordd($1, $2));
+}
+        | vertex_uv_body TANGENT '{' real real real '}'
+{
+  if (DCAST(EggVertexUV, egg_stack.back())->has_tangent()) {
+    eggyywarning("Ignoring repeated tangent");
+  } else {
+    DCAST(EggVertexUV, egg_stack.back())->set_tangent(Normald($4, $5, $6));
+  }
+}
+        | vertex_uv_body BINORMAL '{' real real real '}'
+{
+  if (DCAST(EggVertexUV, egg_stack.back())->has_binormal()) {
+    eggyywarning("Ignoring repeated binormal");
+  } else {
+    DCAST(EggVertexUV, egg_stack.back())->set_binormal(Normald($4, $5, $6));
+  }
 }
         | vertex_uv_body DUV string '{' real real '}'
 {

+ 96 - 8
panda/src/egg2pg/eggLoader.cxx

@@ -371,21 +371,85 @@ make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform,
     // Now, is our parent node a GeomNode, or just an ordinary
     // PandaNode?  If it's a GeomNode, we can add the new Geom directly
     // to our parent; otherwise, we need to create a new node.
+    PT(GeomNode) geom_node;
     if (parent->is_geom_node() && !render_state->_hidden) {
-      DCAST(GeomNode, parent)->add_geom(geom, render_state->_state);
+      geom_node = DCAST(GeomNode, parent);
       
     } else {
-      PT(GeomNode) geom_node = new GeomNode(egg_bin->get_name());
+      geom_node = new GeomNode(egg_bin->get_name());
       if (render_state->_hidden) {
         parent->add_stashed(geom_node);
       } else {
         parent->add_child(geom_node);
       }
-      geom_node->add_geom(geom, render_state->_state);
+    }
+
+    geom_node->add_geom(geom, render_state->_state);
+    if (egg_show_normals) {
+      show_normals(vertex_pool, geom_node);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::show_normals
+//       Access: Private
+//  Description: In the presence of egg-show-normals, generate some
+//               additional geometry to represent the normals,
+//               tangents, and binormals of each vertex.
+////////////////////////////////////////////////////////////////////
+void EggLoader::
+show_normals(EggVertexPool *vertex_pool, GeomNode *geom_node) {
+  PT(GeomPrimitive) primitive = new GeomLines(Geom::UH_static);
+  CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
+  PT(GeomVertexData) vertex_data =
+    new GeomVertexData(vertex_pool->get_name(), format, Geom::UH_static);
+
+  GeomVertexWriter vertex(vertex_data, InternalName::get_vertex());
+  GeomVertexWriter color(vertex_data, InternalName::get_color());
+
+  EggVertexPool::const_iterator vi;
+  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
+    EggVertex *vert = (*vi);
+    LPoint3d pos = vert->get_pos3();
+
+    if (vert->has_normal()) {
+      vertex.add_data3f(LCAST(float, pos));
+      vertex.add_data3f(LCAST(float, pos + vert->get_normal() * egg_normal_scale));
+      color.add_data4f(1.0f, 0.0f, 0.0f, 1.0f);
+      color.add_data4f(1.0f, 0.0f, 0.0f, 1.0f);
+      primitive->add_next_vertices(2);
+      primitive->close_primitive();
+    }
+
+    // Also look for tangents and binormals in each texture coordinate
+    // set.
+    EggVertex::const_uv_iterator uvi;
+    for (uvi = vert->uv_begin(); uvi != vert->uv_end(); ++uvi) {
+      EggVertexUV *uv_obj = (*uvi);
+      if (uv_obj->has_tangent()) {
+        vertex.add_data3f(LCAST(float, pos));
+        vertex.add_data3f(LCAST(float, pos + uv_obj->get_tangent() * egg_normal_scale));
+        color.add_data4f(0.0f, 1.0f, 0.0f, 1.0f);
+        color.add_data4f(0.0f, 1.0f, 0.0f, 1.0f);
+        primitive->add_next_vertices(2);
+        primitive->close_primitive();
+      }
+      if (uv_obj->has_binormal()) {
+        vertex.add_data3f(LCAST(float, pos));
+        vertex.add_data3f(LCAST(float, pos + uv_obj->get_binormal() * egg_normal_scale));
+        color.add_data4f(0.0f, 0.0f, 1.0f, 1.0f);
+        color.add_data4f(0.0f, 0.0f, 1.0f, 1.0f);
+        primitive->add_next_vertices(2);
+        primitive->close_primitive();
+      }
     }
   }
 
-  return;
+  PT(Geom) geom = new Geom;
+  geom->set_vertex_data(vertex_data);
+  geom->add_primitive(primitive);
+  geom_node->add_geom(geom);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1659,8 +1723,8 @@ make_vertex_data(const EggRenderState *render_state,
        Geom::NT_packed_dabc, Geom::C_color);
   }
 
-  vector_string uv_names;
-  vertex_pool->get_uv_names(uv_names);
+  vector_string uv_names, tbn_names;
+  vertex_pool->get_uv_names(uv_names, tbn_names);
   vector_string::const_iterator ni;
   for (ni = uv_names.begin(); ni != uv_names.end(); ++ni) {
     string name = (*ni);
@@ -1669,8 +1733,19 @@ make_vertex_data(const EggRenderState *render_state,
     }
     PT(InternalName) iname = InternalName::get_texcoord_name(name);
     array_format->add_column
-      (iname, 2,
-       Geom::NT_float32, Geom::C_texcoord);
+      (iname, 2, Geom::NT_float32, Geom::C_texcoord);
+  }
+  for (ni = tbn_names.begin(); ni != tbn_names.end(); ++ni) {
+    string name = (*ni);
+    if (name == "default") {
+      name = string();
+    }
+    PT(InternalName) iname = InternalName::get_tangent_name(name);
+    array_format->add_column
+      (iname, 3, Geom::NT_float32, Geom::C_vector);
+    iname = InternalName::get_binormal_name(name);
+    array_format->add_column
+      (iname, 3, Geom::NT_float32, Geom::C_vector);
   }
 
   PT(GeomVertexFormat) temp_format = new GeomVertexFormat(array_format);
@@ -1867,6 +1942,19 @@ make_vertex_data(const EggRenderState *render_state,
           gvw.add_data2f(LCAST(float, duv));
         }
       }
+
+      // Also add the tangent and binormal, if present.
+      if (egg_uv->has_tangent() && egg_uv->has_binormal()) {
+        PT(InternalName) iname = InternalName::get_tangent_name(name);
+        gvw.set_column(iname);
+        if (gvw.has_column()) {
+          LVector3d tangent = egg_uv->get_tangent();
+          LVector3d binormal = egg_uv->get_binormal();
+          gvw.add_data3f(LCAST(float, tangent));
+          gvw.set_column(InternalName::get_binormal_name(name));
+          gvw.add_data3f(LCAST(float, binormal));
+        }          
+      }
     }
 
     if (is_dynamic) {

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

@@ -105,7 +105,9 @@ private:
     GeomPrimitive::ShadeModel _shade_model;
   };
   typedef pmap<PrimitiveUnifier, PT(GeomPrimitive) > Primitives;
-  
+
+  void show_normals(EggVertexPool *vertex_pool, GeomNode *geom_node);  
+
   void make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
                         const LMatrix4d &mat);
   void make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,

+ 26 - 0
panda/src/gobj/internalName.I

@@ -136,6 +136,19 @@ get_tangent() {
   return _tangent;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::get_tangent_name
+//       Access: Published, Static
+//  Description: Returns the InternalName "tangent.name", where name
+//               is the supplied string.  This is the column header
+//               for the tangent associated with the named texture
+//               coordinate set.
+////////////////////////////////////////////////////////////////////
+INLINE PT(InternalName) InternalName::
+get_tangent_name(const string &name) {
+  return get_tangent()->append(name);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: InternalName::get_binormal
 //       Access: Published, Static
@@ -155,6 +168,19 @@ get_binormal() {
   return _binormal;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::get_binormal_name
+//       Access: Published, Static
+//  Description: Returns the InternalName "binormal.name", where name
+//               is the supplied string.  This is the column header
+//               for the binormal associated with the named texture
+//               coordinate set.
+////////////////////////////////////////////////////////////////////
+INLINE PT(InternalName) InternalName::
+get_binormal_name(const string &name) {
+  return get_binormal()->append(name);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: InternalName::get_texcoord
 //       Access: Published, Static

+ 2 - 0
panda/src/gobj/internalName.h

@@ -67,7 +67,9 @@ PUBLISHED:
   INLINE static PT(InternalName) get_vertex();
   INLINE static PT(InternalName) get_normal();
   INLINE static PT(InternalName) get_tangent();
+  INLINE static PT(InternalName) get_tangent_name(const string &name);
   INLINE static PT(InternalName) get_binormal();
+  INLINE static PT(InternalName) get_binormal_name(const string &name);
   INLINE static PT(InternalName) get_texcoord();
   INLINE static PT(InternalName) get_texcoord_name(const string &name);
   INLINE static PT(InternalName) get_color();

+ 12 - 0
pandatool/src/eggbase/eggBase.cxx

@@ -87,6 +87,18 @@ add_normals_options() {
     ("nn", "", 48,
      "Preserve normals exactly as they are.  This is the default.",
      &EggBase::dispatch_normals, NULL, &preserve);
+
+  add_option
+    ("tbn", "name", 48,
+     "Compute tangent and binormal for the named texture coordinate "
+     "set(s).  The name may include wildcard characters such as * and ?; "
+     "use \"*\" to recompute tangent and binormal for all texture coordinate "
+     "sets.  "
+     "The normal must already exist or have been computed via one of the "
+     "above options.  The tangent and binormal are used to implement "
+     "bump mapping and related texture-based lighting effects.  This option "
+     "may be repeated as necessary to name multiple texture coordinate sets.",
+     &EggBase::dispatch_vector_string, NULL, &_tbn_names);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
pandatool/src/eggbase/eggBase.h

@@ -66,6 +66,7 @@ protected:
   };
   NormalsMode _normals_mode;
   double _normals_threshold;
+  vector_string _tbn_names;
 
   bool _got_transform;
   LMatrix4d _transform;

+ 9 - 0
pandatool/src/eggbase/eggWriter.cxx

@@ -20,6 +20,7 @@
 
 #include "string_utils.h"
 #include "compose_matrix.h"
+#include "globPattern.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggWriter::Constructor
@@ -161,6 +162,14 @@ post_process_egg_file() {
     // Do nothing.
     break;
   }
+
+  for (vector_string::const_iterator si = _tbn_names.begin();
+       si != _tbn_names.end();
+       ++si) {
+    GlobPattern uv_name(*si);
+    nout << "Computing tangent and binormal for \"" << uv_name << "\"\n";
+    _data->recompute_tangent_binormal(uv_name);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

Some files were not shown because too many files changed in this diff