浏览代码

egg loader reads nurbs surfaces and curves into SheetNodes and RopeNodes

David Rose 22 年之前
父节点
当前提交
c69cc5c96f

+ 2 - 2
panda/src/egg/eggNurbsCurve.h

@@ -66,9 +66,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    EggPrimitive::init_type();
+    EggCurve::init_type();
     register_type(_type_handle, "EggNurbsCurve",
     register_type(_type_handle, "EggNurbsCurve",
-                  EggPrimitive::get_class_type());
+                  EggCurve::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

+ 2 - 2
panda/src/egg/eggNurbsSurface.h

@@ -100,9 +100,9 @@ public:
     return _type_handle;
     return _type_handle;
   }
   }
   static void init_type() {
   static void init_type() {
-    EggPrimitive::init_type();
+    EggSurface::init_type();
     register_type(_type_handle, "EggNurbsSurface",
     register_type(_type_handle, "EggNurbsSurface",
-                  EggPrimitive::get_class_type());
+                  EggSurface::get_class_type());
   }
   }
   virtual TypeHandle get_type() const {
   virtual TypeHandle get_type() const {
     return get_class_type();
     return get_class_type();

+ 12 - 1
panda/src/egg2pg/characterMaker.cxx

@@ -29,6 +29,8 @@
 #include "characterSlider.h"
 #include "characterSlider.h"
 #include "character.h"
 #include "character.h"
 #include "transformState.h"
 #include "transformState.h"
+#include "eggSurface.h"
+#include "eggCurve.h"
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: CharacterMaker::Construtor
 //     Function: CharacterMaker::Construtor
@@ -239,7 +241,16 @@ make_geometry(EggNode *egg_node) {
     if (!egg_primitive->empty()) {
     if (!egg_primitive->empty()) {
       EggGroupNode *prim_home = determine_primitive_home(egg_primitive);
       EggGroupNode *prim_home = determine_primitive_home(egg_primitive);
 
 
-      if (prim_home == NULL) {
+      if (prim_home == (EggGroupNode *)NULL &&
+          (egg_primitive->is_of_type(EggSurface::get_class_type()) ||
+           egg_primitive->is_of_type(EggCurve::get_class_type()))) {
+        // If the primitive would be dynamic but is a parametric
+        // primitive, we can't animate it anyway, so just put the
+        // whole thing under the primitive's parent node.
+        prim_home = egg_primitive->get_parent();
+      }
+
+      if (prim_home == (EggGroupNode *)NULL) {
         // This is a totally dynamic primitive that lives under the
         // This is a totally dynamic primitive that lives under the
         // character's node.
         // character's node.
         make_dynamic_primitive(egg_primitive, _egg_root);
         make_dynamic_primitive(egg_primitive, _egg_root);

+ 10 - 4
panda/src/egg2pg/config_egg2pg.cxx

@@ -53,10 +53,16 @@ bool egg_flatten_siblings = config_egg2pg.GetBool("egg-flatten-siblings", false)
 bool egg_show_collision_solids = config_egg2pg.GetBool("egg-show-collision-solids", false);
 bool egg_show_collision_solids = config_egg2pg.GetBool("egg-show-collision-solids", false);
 
 
 // When this is true, a <NurbsCurve> entry appearing in an egg file
 // When this is true, a <NurbsCurve> entry appearing in an egg file
-// will load a ClassicNurbsCurve object instead of the default, a
-// NurbsCurve object.  This only makes a difference when the NURBS++
-// library is available, in which case the default, NurbsCurve, is
-// actually a NurbsPPCurve object.
+// will load as a NurbsCurve or ClassicNurbsCurve object (see below).
+// When this is false, it will load a RopeNode instead, which uses the
+// new NurbsCurveEvaluator interface.
+bool egg_load_old_curves = config_egg2pg.GetBool("egg-load-old-curves", true);
+
+// When this is true (and the above is also true), a <NurbsCurve>
+// entry appearing in an egg file will load a ClassicNurbsCurve object
+// instead of the default, a NurbsCurve object.  This only makes a
+// difference when the NURBS++ library is available, in which case the
+// default, NurbsCurve, is actually a NurbsPPCurve object.
 bool egg_load_classic_nurbs_curves = config_egg2pg.GetBool("egg-load-classic-nurbs-curves", false);
 bool egg_load_classic_nurbs_curves = config_egg2pg.GetBool("egg-load-classic-nurbs-curves", false);
 
 
 // When this is true, certain kinds of recoverable errors (not syntax
 // When this is true, certain kinds of recoverable errors (not syntax

+ 1 - 0
panda/src/egg2pg/config_egg2pg.h

@@ -52,6 +52,7 @@ extern EXPCL_PANDAEGG bool egg_ignore_decals;
 extern EXPCL_PANDAEGG bool egg_flatten;
 extern EXPCL_PANDAEGG bool egg_flatten;
 extern EXPCL_PANDAEGG bool egg_flatten_siblings;
 extern EXPCL_PANDAEGG bool egg_flatten_siblings;
 extern EXPCL_PANDAEGG bool egg_show_collision_solids;
 extern EXPCL_PANDAEGG bool egg_show_collision_solids;
+extern EXPCL_PANDAEGG bool egg_load_old_curves;
 extern EXPCL_PANDAEGG bool egg_load_classic_nurbs_curves;
 extern EXPCL_PANDAEGG bool egg_load_classic_nurbs_curves;
 extern EXPCL_PANDAEGG bool egg_accept_errors;
 extern EXPCL_PANDAEGG bool egg_accept_errors;
 extern EXPCL_PANDAEGG EggRenderMode::AlphaMode egg_alpha_mode;
 extern EXPCL_PANDAEGG EggRenderMode::AlphaMode egg_alpha_mode;

+ 402 - 134
panda/src/egg2pg/eggLoader.cxx

@@ -34,6 +34,8 @@
 #include "depthTestAttrib.h"
 #include "depthTestAttrib.h"
 #include "depthWriteAttrib.h"
 #include "depthWriteAttrib.h"
 #include "materialAttrib.h"
 #include "materialAttrib.h"
+#include "texMatrixAttrib.h"
+#include "colorAttrib.h"
 #include "materialPool.h"
 #include "materialPool.h"
 #include "geomNode.h"
 #include "geomNode.h"
 #include "sequenceNode.h"
 #include "sequenceNode.h"
@@ -46,6 +48,7 @@
 #include "eggPoint.h"
 #include "eggPoint.h"
 #include "eggTextureCollection.h"
 #include "eggTextureCollection.h"
 #include "eggNurbsCurve.h"
 #include "eggNurbsCurve.h"
+#include "eggNurbsSurface.h"
 #include "eggGroupNode.h"
 #include "eggGroupNode.h"
 #include "eggGroup.h"
 #include "eggGroup.h"
 #include "eggPolygon.h"
 #include "eggPolygon.h"
@@ -67,6 +70,10 @@
 #include "nurbsCurve.h"
 #include "nurbsCurve.h"
 #include "classicNurbsCurve.h"
 #include "classicNurbsCurve.h"
 #include "nurbsCurveInterface.h"
 #include "nurbsCurveInterface.h"
+#include "nurbsCurveEvaluator.h"
+#include "nurbsSurfaceEvaluator.h"
+#include "ropeNode.h"
+#include "sheetNode.h"
 #include "look_at.h"
 #include "look_at.h"
 
 
 #include <ctype.h>
 #include <ctype.h>
@@ -238,66 +245,75 @@ make_nonindexed_primitive(EggPrimitive *egg_prim, PandaNode *parent,
     mat = egg_prim->get_vertex_to_node();
     mat = egg_prim->get_vertex_to_node();
   }
   }
 
 
-  BuilderPrim bprim;
-  bprim.set_type(BPT_poly);
-  if (egg_prim->is_of_type(EggPoint::get_class_type())) {
-    bprim.set_type(BPT_point);
-  }
-
-  if (egg_prim->has_normal()) {
-    Normald norm = egg_prim->get_normal() * mat;
-    norm.normalize();
-    bprim.set_normal(LCAST(float, norm));
-  }
-  if (egg_prim->has_color() && !egg_false_color) {
-    bprim.set_color(egg_prim->get_color());
-  }
+  if (egg_prim->is_of_type(EggNurbsCurve::get_class_type())) {
+    make_nurbs_curve(DCAST(EggNurbsCurve, egg_prim), parent, mat);
 
 
-  bool has_vert_color = true;
-  EggPrimitive::const_iterator vi;
-  for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
-    EggVertex *egg_vert = *vi;
-    if (egg_vert->get_num_dimensions() != 3) {
-      egg2pg_cat.error()
-        << "Vertex " << egg_vert->get_pool()->get_name() 
-        << ":" << egg_vert->get_index() << " has dimension " 
-        << egg_vert->get_num_dimensions() << "\n";
-    } else {
-      BuilderVertex bvert(LCAST(float, egg_vert->get_pos3() * mat));
+  } else if (egg_prim->is_of_type(EggNurbsSurface::get_class_type())) {
+    make_nurbs_surface(DCAST(EggNurbsSurface, egg_prim), parent, mat);
 
 
-      if (egg_vert->has_normal()) {
-        Normald norm = egg_vert->get_normal() * mat;
-        norm.normalize();
-        bvert.set_normal(LCAST(float, norm));
-      }
-      if (egg_vert->has_color() && !egg_false_color) {
-        bvert.set_color(egg_vert->get_color());
+  } else {
+    // A normal primitive: polygon or point.
+    BuilderPrim bprim;
+    bprim.set_type(BPT_poly);
+    if (egg_prim->is_of_type(EggPoint::get_class_type())) {
+      bprim.set_type(BPT_point);
+    }
+    
+    if (egg_prim->has_normal()) {
+      Normald norm = egg_prim->get_normal() * mat;
+      norm.normalize();
+      bprim.set_normal(LCAST(float, norm));
+    }
+    if (egg_prim->has_color() && !egg_false_color) {
+      bprim.set_color(egg_prim->get_color());
+    }
+    
+    bool has_vert_color = true;
+    EggPrimitive::const_iterator vi;
+    for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
+      EggVertex *egg_vert = *vi;
+      if (egg_vert->get_num_dimensions() != 3) {
+        egg2pg_cat.error()
+          << "Vertex " << egg_vert->get_pool()->get_name() 
+          << ":" << egg_vert->get_index() << " has dimension " 
+          << egg_vert->get_num_dimensions() << "\n";
       } else {
       } else {
-        // If any vertex doesn't have a color, we can't use any of the
-        // vertex colors.
-        has_vert_color = false;
-      }
-      if (egg_vert->has_uv()) {
-        TexCoordd uv = egg_vert->get_uv();
-        if (egg_prim->has_texture() &&
-            egg_prim->get_texture()->has_transform()) {
-          // If we have a texture matrix, apply it.
-          uv = uv * egg_prim->get_texture()->get_transform();
+        BuilderVertex bvert(LCAST(float, egg_vert->get_pos3() * mat));
+        
+        if (egg_vert->has_normal()) {
+          Normald norm = egg_vert->get_normal() * mat;
+          norm.normalize();
+          bvert.set_normal(LCAST(float, norm));
+        }
+        if (egg_vert->has_color() && !egg_false_color) {
+          bvert.set_color(egg_vert->get_color());
+        } else {
+          // If any vertex doesn't have a color, we can't use any of the
+          // vertex colors.
+          has_vert_color = false;
+        }
+        if (egg_vert->has_uv()) {
+          TexCoordd uv = egg_vert->get_uv();
+          if (egg_prim->has_texture() &&
+              egg_prim->get_texture()->has_transform()) {
+            // If we have a texture matrix, apply it.
+            uv = uv * egg_prim->get_texture()->get_transform();
+          }
+          bvert.set_texcoord(LCAST(float, uv));
         }
         }
-        bvert.set_texcoord(LCAST(float, uv));
+        
+        bprim.add_vertex(bvert);
       }
       }
-    
-      bprim.add_vertex(bvert);
     }
     }
-  }
 
 
-  // Finally, if the primitive didn't have a color, and it didn't have
-  // vertex color, make it white.
-  if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) {
-    bprim.set_color(Colorf(1.0, 1.0, 1.0, 1.0));
-  }
+    // Finally, if the primitive didn't have a color, and it didn't have
+    // vertex color, make it white.
+    if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) {
+      bprim.set_color(Colorf(1.0, 1.0, 1.0, 1.0));
+    }
 
 
-  _builder.add_prim(bucket, bprim);
+    _builder.add_prim(bucket, bprim);
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -430,6 +446,339 @@ make_indexed_primitive(EggPrimitive *egg_prim, PandaNode *parent,
   _builder.add_prim(bucket, bprim);
   _builder.add_prim(bucket, bprim);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::make_nurbs_curve
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void EggLoader::
+make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent, 
+                 const LMatrix4d &mat) {
+  if (egg_load_old_curves) {
+    // Make a NurbsCurve instead of a RopeNode (old interface).
+    make_old_nurbs_curve(egg_curve, parent, mat);
+    return;
+  }
+
+  assert(parent != NULL);
+  assert(!parent->is_geom_node());
+
+  PT(NurbsCurveEvaluator) nurbs = new NurbsCurveEvaluator;
+
+  if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
+    egg2pg_cat.error()
+      << "Invalid NURBSCurve order for " << egg_curve->get_name() << ": "
+      << egg_curve->get_order() << "\n";
+    _error = true;
+    return;
+  }
+
+  nurbs->set_order(egg_curve->get_order());
+
+  nurbs->reset(egg_curve->size());
+  EggPrimitive::const_iterator pi;
+  int vi = 0;
+  for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
+    EggVertex *egg_vertex = (*pi);
+    nurbs->set_vertex(vi, LCAST(float, egg_vertex->get_pos4() * mat));
+    Colorf color = egg_vertex->get_color();
+    nurbs->set_extended_vertex(vi, 0, color[0]);
+    nurbs->set_extended_vertex(vi, 1, color[1]);
+    nurbs->set_extended_vertex(vi, 2, color[2]);
+    nurbs->set_extended_vertex(vi, 3, color[3]);
+    vi++;
+  }
+
+  int num_knots = egg_curve->get_num_knots();
+  if (num_knots != nurbs->get_num_knots()) {
+    egg2pg_cat.error()
+      << "Invalid NURBSCurve number of knots for "
+      << egg_curve->get_name() << ": got " << num_knots
+      << " knots, expected " << nurbs->get_num_knots() << "\n";
+    _error = true;
+    return;
+  }
+
+  for (int i = 0; i < num_knots; i++) {
+    nurbs->set_knot(i, egg_curve->get_knot(i));
+  }
+
+  /*
+  switch (egg_curve->get_curve_type()) {
+  case EggCurve::CT_xyz:
+    curve->set_curve_type(PCT_XYZ);
+    break;
+
+  case EggCurve::CT_hpr:
+    curve->set_curve_type(PCT_HPR);
+    break;
+
+  case EggCurve::CT_t:
+    curve->set_curve_type(PCT_T);
+    break;
+
+  default:
+    break;
+  }
+  */
+
+  PT(RopeNode) rope = new RopeNode(egg_curve->get_name());
+  rope->set_curve(nurbs);
+
+  // Respect the subdivision values in the egg file, if any.
+  if (egg_curve->get_subdiv() != 0) {
+    int subdiv_per_segment = 
+      (int)((egg_curve->get_subdiv() + 0.5) / nurbs->get_num_segments());
+    rope->set_num_subdiv(subdiv_per_segment);
+  }
+
+  // Now get the attributes to apply to the rope.  We create a
+  // BuilderBucket for this purpose, so we can call setup_bucket(),
+  // but all we do with this bucket is immediately extract the state
+  // from it.
+  BuilderBucket bucket;
+  setup_bucket(bucket, parent, egg_curve);
+
+  rope->set_state(bucket._state);
+
+  // If we have a texture matrix, we have to apply that explicitly
+  // (the UV's are computed on the fly, so we can't precompute the
+  // texture matrix into them).
+  if (egg_curve->has_texture()) {
+    rope->set_uv_mode(RopeNode::UV_parametric);
+
+    PT(EggTexture) egg_tex = egg_curve->get_texture();
+    if (egg_tex->has_transform()) {
+      // Expand the 2-d matrix to a 3-d matrix.
+      const LMatrix3d &mat3 = egg_tex->get_transform();
+      LMatrix4f mat4(mat3(0, 0), mat3(0, 1), 0.0f, mat3(0, 2),
+                     mat3(1, 0), mat3(1, 1), 0.0f, mat3(1, 2),
+                     0.0f, 0.0f, 1.0f, 0.0f,
+                     mat3(2, 0), mat3(2, 1), 0.0f, mat3(2, 2));
+      rope->set_attrib(TexMatrixAttrib::make(mat4));
+    }
+  }
+  if (egg_curve->has_vertex_color()) {
+    // If the curve had individual vertex color, enable it.
+    rope->set_use_vertex_color(true);
+  } else if (egg_curve->has_color()) {
+    // Otherwise, if the curve has overall color, apply it.
+    rope->set_attrib(ColorAttrib::make_flat(egg_curve->get_color()));
+  }
+
+  parent->add_child(rope);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::make_old_nurbs_curve
+//       Access: Private
+//  Description: This deprecated interface creates a NurbsCurve (or a
+//               ClassicNurbsCurve) object for the EggNurbsCurve
+//               entry.  It will eventually be removed in favor of the
+//               above, which creates a RopeNode.
+////////////////////////////////////////////////////////////////////
+void EggLoader::
+make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
+                     const LMatrix4d &mat) {
+  assert(parent != NULL);
+  assert(!parent->is_geom_node());
+
+  PT(ParametricCurve) curve;
+
+  if (egg_load_classic_nurbs_curves) {
+    curve = new ClassicNurbsCurve;
+  } else {
+    curve = new NurbsCurve;
+  }
+
+  NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
+  nassertv(nurbs != (NurbsCurveInterface *)NULL);
+
+  if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
+    egg2pg_cat.error()
+      << "Invalid NURBSCurve order for " << egg_curve->get_name() << ": "
+      << egg_curve->get_order() << "\n";
+    _error = true;
+    return;
+  }
+
+  nurbs->set_order(egg_curve->get_order());
+
+  EggPrimitive::const_iterator pi;
+  for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
+    nurbs->append_cv(LCAST(float, (*pi)->get_pos4() * mat));
+  }
+
+  int num_knots = egg_curve->get_num_knots();
+  if (num_knots != nurbs->get_num_knots()) {
+    egg2pg_cat.error()
+      << "Invalid NURBSCurve number of knots for "
+      << egg_curve->get_name() << ": got " << num_knots
+      << " knots, expected " << nurbs->get_num_knots() << "\n";
+    _error = true;
+    return;
+  }
+
+  for (int i = 0; i < num_knots; i++) {
+    nurbs->set_knot(i, egg_curve->get_knot(i));
+  }
+
+  switch (egg_curve->get_curve_type()) {
+  case EggCurve::CT_xyz:
+    curve->set_curve_type(PCT_XYZ);
+    break;
+
+  case EggCurve::CT_hpr:
+    curve->set_curve_type(PCT_HPR);
+    break;
+
+  case EggCurve::CT_t:
+    curve->set_curve_type(PCT_T);
+    break;
+
+  default:
+    break;
+  }
+  curve->set_name(egg_curve->get_name());
+
+  if (!curve->recompute()) {
+    egg2pg_cat.error()
+      << "Invalid NURBSCurve " << egg_curve->get_name() << "\n";
+    _error = true;
+    return;
+  }
+
+  parent->add_child(curve);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::make_nurbs_surface
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+void EggLoader::
+make_nurbs_surface(EggNurbsSurface *egg_surface, PandaNode *parent,
+                   const LMatrix4d &mat) {
+  assert(parent != NULL);
+  assert(!parent->is_geom_node());
+
+  PT(NurbsSurfaceEvaluator) nurbs = new NurbsSurfaceEvaluator;
+
+  if (egg_surface->get_u_order() < 1 || egg_surface->get_u_order() > 4) {
+    egg2pg_cat.error()
+      << "Invalid NURBSSurface U order for " << egg_surface->get_name() << ": "
+      << egg_surface->get_u_order() << "\n";
+    _error = true;
+    return;
+  }
+
+  if (egg_surface->get_v_order() < 1 || egg_surface->get_v_order() > 4) {
+    egg2pg_cat.error()
+      << "Invalid NURBSSurface V order for " << egg_surface->get_name() << ": "
+      << egg_surface->get_v_order() << "\n";
+    _error = true;
+    return;
+  }
+
+  nurbs->set_u_order(egg_surface->get_u_order());
+  nurbs->set_v_order(egg_surface->get_v_order());
+
+  int num_u_vertices = egg_surface->get_num_u_cvs();
+  int num_v_vertices = egg_surface->get_num_v_cvs();
+  nurbs->reset(num_u_vertices, num_v_vertices);
+  for (int ui = 0; ui < num_u_vertices; ui++) {
+    for (int vi = 0; vi < num_v_vertices; vi++) {
+      int i = egg_surface->get_vertex_index(ui, vi);
+      EggVertex *egg_vertex = egg_surface->get_vertex(i);
+      nurbs->set_vertex(ui, vi, LCAST(float, egg_vertex->get_pos4() * mat));
+
+      Colorf color = egg_vertex->get_color();
+      nurbs->set_extended_vertex(ui, vi, 0, color[0]);
+      nurbs->set_extended_vertex(ui, vi, 1, color[1]);
+      nurbs->set_extended_vertex(ui, vi, 2, color[2]);
+      nurbs->set_extended_vertex(ui, vi, 3, color[3]);
+    }
+  }
+
+  int num_u_knots = egg_surface->get_num_u_knots();
+  if (num_u_knots != nurbs->get_num_u_knots()) {
+    egg2pg_cat.error()
+      << "Invalid NURBSSurface number of U knots for "
+      << egg_surface->get_name() << ": got " << num_u_knots
+      << " knots, expected " << nurbs->get_num_u_knots() << "\n";
+    _error = true;
+    return;
+  }
+
+  int num_v_knots = egg_surface->get_num_v_knots();
+  if (num_v_knots != nurbs->get_num_v_knots()) {
+    egg2pg_cat.error()
+      << "Invalid NURBSSurface number of U knots for "
+      << egg_surface->get_name() << ": got " << num_v_knots
+      << " knots, expected " << nurbs->get_num_v_knots() << "\n";
+    _error = true;
+    return;
+  }
+
+  int i;
+  for (i = 0; i < num_u_knots; i++) {
+    nurbs->set_u_knot(i, egg_surface->get_u_knot(i));
+  }
+  for (i = 0; i < num_v_knots; i++) {
+    nurbs->set_v_knot(i, egg_surface->get_v_knot(i));
+  }
+
+  PT(SheetNode) sheet = new SheetNode(egg_surface->get_name());
+  sheet->set_surface(nurbs);
+
+  // Respect the subdivision values in the egg file, if any.
+  if (egg_surface->get_u_subdiv() != 0) {
+    int u_subdiv_per_segment = 
+      (int)((egg_surface->get_u_subdiv() + 0.5) / nurbs->get_num_u_segments());
+    sheet->set_num_u_subdiv(u_subdiv_per_segment);
+  }
+  if (egg_surface->get_v_subdiv() != 0) {
+    int v_subdiv_per_segment = 
+      (int)((egg_surface->get_v_subdiv() + 0.5) / nurbs->get_num_v_segments());
+    sheet->set_num_v_subdiv(v_subdiv_per_segment);
+  }
+
+  // Now get the attributes to apply to the sheet.  We create a
+  // BuilderBucket for this purpose, so we can call setup_bucket(),
+  // but all we do with this bucket is immediately extract the state
+  // from it.
+  BuilderBucket bucket;
+  setup_bucket(bucket, parent, egg_surface);
+
+  sheet->set_state(bucket._state);
+
+  // If we have a texture matrix, we have to apply that explicitly
+  // (the UV's are computed on the fly, so we can't precompute the
+  // texture matrix into them).
+  if (egg_surface->has_texture()) {
+    PT(EggTexture) egg_tex = egg_surface->get_texture();
+    if (egg_tex->has_transform()) {
+      // Expand the 2-d matrix to a 3-d matrix.
+      const LMatrix3d &mat3 = egg_tex->get_transform();
+      LMatrix4f mat4(mat3(0, 0), mat3(0, 1), 0.0f, mat3(0, 2),
+                     mat3(1, 0), mat3(1, 1), 0.0f, mat3(1, 2),
+                     0.0f, 0.0f, 1.0f, 0.0f,
+                     mat3(2, 0), mat3(2, 1), 0.0f, mat3(2, 2));
+      sheet->set_attrib(TexMatrixAttrib::make(mat4));
+    }
+  }
+
+  if (egg_surface->has_vertex_color()) {
+    // If the surface had individual vertex color, enable it.
+    sheet->set_use_vertex_color(true);
+  } else if (egg_surface->has_color()) {
+    // Otherwise, if the surface has overall color, apply it.
+    sheet->set_attrib(ColorAttrib::make_flat(egg_surface->get_color()));
+  }
+
+  parent->add_child(sheet);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::load_textures
 //     Function: EggLoader::load_textures
 //       Access: Private
 //       Access: Private
@@ -1146,9 +1495,7 @@ setup_bucket(BuilderBucket &bucket, PandaNode *parent,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PandaNode *EggLoader::
 PandaNode *EggLoader::
 make_node(EggNode *egg_node, PandaNode *parent) {
 make_node(EggNode *egg_node, PandaNode *parent) {
-  if (egg_node->is_of_type(EggNurbsCurve::get_class_type())) {
-    return make_node(DCAST(EggNurbsCurve, egg_node), parent);
-  } else if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
+  if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
     return make_node(DCAST(EggPrimitive, egg_node), parent);
     return make_node(DCAST(EggPrimitive, egg_node), parent);
   } else if (egg_node->is_of_type(EggBin::get_class_type())) {
   } else if (egg_node->is_of_type(EggBin::get_class_type())) {
     return make_node(DCAST(EggBin, egg_node), parent);
     return make_node(DCAST(EggBin, egg_node), parent);
@@ -1163,85 +1510,6 @@ make_node(EggNode *egg_node, PandaNode *parent) {
   return (PandaNode *)NULL;
   return (PandaNode *)NULL;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: EggLoader::make_node (EggNurbsCurve)
-//       Access: Private
-//  Description:
-////////////////////////////////////////////////////////////////////
-PandaNode *EggLoader::
-make_node(EggNurbsCurve *egg_curve, PandaNode *parent) {
-  assert(parent != NULL);
-  assert(!parent->is_geom_node());
-
-  PT(ParametricCurve) curve;
-
-  if (egg_load_classic_nurbs_curves) {
-    curve = new ClassicNurbsCurve;
-  } else {
-    curve = new NurbsCurve;
-  }
-
-  NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
-  nassertr(nurbs != (NurbsCurveInterface *)NULL, (PandaNode *)NULL);
-
-  if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
-    egg2pg_cat.error()
-      << "Invalid NURBSCurve order for " << egg_curve->get_name() << ": "
-      << egg_curve->get_order() << "\n";
-    _error = true;
-    return (PandaNode *)NULL;
-  }
-
-  nurbs->set_order(egg_curve->get_order());
-
-  EggPrimitive::const_iterator pi;
-  for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
-    nurbs->append_cv(LCAST(float, (*pi)->get_pos4()));
-  }
-
-  int num_knots = egg_curve->get_num_knots();
-  if (num_knots != nurbs->get_num_knots()) {
-    egg2pg_cat.error()
-      << "Invalid NURBSCurve number of knots for "
-      << egg_curve->get_name() << ": got " << num_knots
-      << " knots, expected " << nurbs->get_num_knots() << "\n";
-    _error = true;
-    return (PandaNode *)NULL;
-  }
-
-  for (int i = 0; i < num_knots; i++) {
-    nurbs->set_knot(i, egg_curve->get_knot(i));
-  }
-
-  switch (egg_curve->get_curve_type()) {
-  case EggCurve::CT_xyz:
-    curve->set_curve_type(PCT_XYZ);
-    break;
-
-  case EggCurve::CT_hpr:
-    curve->set_curve_type(PCT_HPR);
-    break;
-
-  case EggCurve::CT_t:
-    curve->set_curve_type(PCT_T);
-    break;
-
-  default:
-    break;
-  }
-  curve->set_name(egg_curve->get_name());
-
-  if (!curve->recompute()) {
-    egg2pg_cat.error()
-      << "Invalid NURBSCurve " << egg_curve->get_name() << "\n";
-    _error = true;
-    return (PandaNode *)NULL;
-  }
-
-  parent->add_child(curve);
-  return curve;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::make_node (EggPrimitive)
 //     Function: EggLoader::make_node (EggPrimitive)
 //       Access: Private
 //       Access: Private

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

@@ -41,6 +41,7 @@ class EggNode;
 class EggBin;
 class EggBin;
 class EggTable;
 class EggTable;
 class EggNurbsCurve;
 class EggNurbsCurve;
+class EggNurbsSurface;
 class EggPrimitive;
 class EggPrimitive;
 class EggPolygon;
 class EggPolygon;
 class EggMaterial;
 class EggMaterial;
@@ -81,6 +82,13 @@ private:
     CPT(RenderAttrib) _apply;
     CPT(RenderAttrib) _apply;
   };
   };
 
 
+  void make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
+                        const LMatrix4d &mat);
+  void make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
+                            const LMatrix4d &mat);
+  void make_nurbs_surface(EggNurbsSurface *egg_surface, PandaNode *parent,
+                          const LMatrix4d &mat);
+
   void load_textures();
   void load_textures();
   bool load_texture(TextureDef &def, const EggTexture *egg_tex);
   bool load_texture(TextureDef &def, const EggTexture *egg_tex);
   void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex);
   void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex);
@@ -93,7 +101,6 @@ private:
                     EggPrimitive *egg_prim);
                     EggPrimitive *egg_prim);
 
 
   PandaNode *make_node(EggNode *egg_node, PandaNode *parent);
   PandaNode *make_node(EggNode *egg_node, PandaNode *parent);
-  PandaNode *make_node(EggNurbsCurve *egg_curve, PandaNode *parent);
   PandaNode *make_node(EggPrimitive *egg_prim, PandaNode *parent);
   PandaNode *make_node(EggPrimitive *egg_prim, PandaNode *parent);
   PandaNode *make_node(EggBin *egg_bin, PandaNode *parent);
   PandaNode *make_node(EggBin *egg_bin, PandaNode *parent);
   PandaNode *make_node(EggGroup *egg_group, PandaNode *parent);
   PandaNode *make_node(EggGroup *egg_group, PandaNode *parent);

+ 5 - 2
panda/src/parametrics/Sources.pp

@@ -27,6 +27,7 @@
     parametricCurveCollection.I parametricCurveCollection.h  \
     parametricCurveCollection.I parametricCurveCollection.h  \
     piecewiseCurve.h \
     piecewiseCurve.h \
     ropeNode.I ropeNode.h \
     ropeNode.I ropeNode.h \
+    sheetNode.I sheetNode.h \
     $[if $[HAVE_NURBSPP], nurbsPPCurve.cxx nurbsPPCurve.h]
     $[if $[HAVE_NURBSPP], nurbsPPCurve.cxx nurbsPPCurve.h]
 
 
 
 
@@ -43,7 +44,8 @@
     nurbsVertex.cxx \
     nurbsVertex.cxx \
     parametricCurve.cxx parametricCurveCollection.cxx  \
     parametricCurve.cxx parametricCurveCollection.cxx  \
     piecewiseCurve.cxx \
     piecewiseCurve.cxx \
-    ropeNode.cxx
+    ropeNode.cxx \
+    sheetNode.cxx
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     classicNurbsCurve.I classicNurbsCurve.h \
     classicNurbsCurve.I classicNurbsCurve.h \
@@ -65,7 +67,8 @@
     parametricCurve.h \
     parametricCurve.h \
     parametricCurveCollection.I parametricCurveCollection.h \
     parametricCurveCollection.I parametricCurveCollection.h \
     piecewiseCurve.h \
     piecewiseCurve.h \
-    ropeNode.I ropeNode.h
+    ropeNode.I ropeNode.h \
+    sheetNode.I sheetNode.h
 
 
   #define IGATESCAN all
   #define IGATESCAN all
 
 

+ 3 - 0
panda/src/parametrics/config_parametrics.cxx

@@ -27,6 +27,7 @@
 #include "parametricCurveDrawer.h"
 #include "parametricCurveDrawer.h"
 #include "piecewiseCurve.h"
 #include "piecewiseCurve.h"
 #include "ropeNode.h"
 #include "ropeNode.h"
+#include "sheetNode.h"
 
 
 #ifdef HAVE_NURBSPP
 #ifdef HAVE_NURBSPP
 #include "nurbsPPCurve.h"
 #include "nurbsPPCurve.h"
@@ -48,6 +49,7 @@ ConfigureFn(config_parametrics) {
   ParametricCurveDrawer::init_type();
   ParametricCurveDrawer::init_type();
   PiecewiseCurve::init_type();
   PiecewiseCurve::init_type();
   RopeNode::init_type();
   RopeNode::init_type();
+  SheetNode::init_type();
 
 
 #ifdef HAVE_NURBSPP
 #ifdef HAVE_NURBSPP
   NurbsPPCurve::init_type();
   NurbsPPCurve::init_type();
@@ -58,6 +60,7 @@ ConfigureFn(config_parametrics) {
   CubicCurveseg::register_with_read_factory();
   CubicCurveseg::register_with_read_factory();
   HermiteCurve::register_with_read_factory();
   HermiteCurve::register_with_read_factory();
   RopeNode::register_with_read_factory();
   RopeNode::register_with_read_factory();
+  SheetNode::register_with_read_factory();
 }
 }
 
 
 const DSearchPath &
 const DSearchPath &

+ 16 - 2
panda/src/parametrics/nurbsCurveEvaluator.I

@@ -157,7 +157,7 @@ set_vertex_space(int i, const string &space) {
 //               ordinary vertex must be set first, before the
 //               ordinary vertex must be set first, before the
 //               extended vertices can be set.
 //               extended vertices can be set.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void NurbsCurveEvaluator::
+INLINE void NurbsCurveEvaluator::
 set_extended_vertex(int i, int d, float value) {
 set_extended_vertex(int i, int d, float value) {
   nassertv(i >= 0 && i < (int)_vertices.size());
   nassertv(i >= 0 && i < (int)_vertices.size());
   _vertices[i].set_extended_vertex(d, value);
   _vertices[i].set_extended_vertex(d, value);
@@ -171,7 +171,7 @@ set_extended_vertex(int i, int d, float value) {
 //               for the indicated dimension, or 0.0 if nothing has
 //               for the indicated dimension, or 0.0 if nothing has
 //               been set.
 //               been set.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-float NurbsCurveEvaluator::
+INLINE float NurbsCurveEvaluator::
 get_extended_vertex(int i, int d) const {
 get_extended_vertex(int i, int d) const {
   nassertr(i >= 0 && i < (int)_vertices.size(), 0.0f);
   nassertr(i >= 0 && i < (int)_vertices.size(), 0.0f);
   return _vertices[i].get_extended_vertex(d);
   return _vertices[i].get_extended_vertex(d);
@@ -187,3 +187,17 @@ INLINE int NurbsCurveEvaluator::
 get_num_knots() const {
 get_num_knots() const {
   return (int)_vertices.size() + _order;
   return (int)_vertices.size() + _order;
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsCurveEvaluator::get_num_segments
+//       Access: Published
+//  Description: Returns the number of piecewise continuous segments
+//               in the curve.  This is based on the knot vector.
+////////////////////////////////////////////////////////////////////
+INLINE int NurbsCurveEvaluator::
+get_num_segments() const {
+  if (_basis_dirty) {
+    ((NurbsCurveEvaluator *)this)->recompute_basis();
+  }
+  return _basis.get_num_segments();
+}

+ 1 - 1
panda/src/parametrics/nurbsCurveEvaluator.cxx

@@ -227,7 +227,7 @@ recompute_basis() {
   _basis.clear(_order);
   _basis.clear(_order);
   if ((int)_vertices.size() > _order - 1) {
   if ((int)_vertices.size() > _order - 1) {
     int min_knot = _order;
     int min_knot = _order;
-    int max_knot = (int)_vertices.size() + 1;
+    int max_knot = (int)_vertices.size();
     
     
     for (int i = min_knot; i <= max_knot; i++) {
     for (int i = min_knot; i <= max_knot; i++) {
       nassertv(i - 1 >= 0 && i < (int)_knots.size());
       nassertv(i - 1 >= 0 && i < (int)_knots.size());

+ 2 - 0
panda/src/parametrics/nurbsCurveEvaluator.h

@@ -71,6 +71,8 @@ PUBLISHED:
   void set_knot(int i, float knot);
   void set_knot(int i, float knot);
   float get_knot(int i) const;
   float get_knot(int i) const;
 
 
+  INLINE int get_num_segments() const;
+
   PT(NurbsCurveResult) evaluate(const NodePath &rel_to = NodePath()) const;
   PT(NurbsCurveResult) evaluate(const NodePath &rel_to = NodePath()) const;
 
 
 public:
 public:

+ 32 - 2
panda/src/parametrics/nurbsSurfaceEvaluator.I

@@ -202,7 +202,7 @@ set_vertex_space(int ui, int vi, const string &space) {
 //               ordinary vertex must be set first, before the
 //               ordinary vertex must be set first, before the
 //               extended vertices can be set.
 //               extended vertices can be set.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-void NurbsSurfaceEvaluator::
+INLINE void NurbsSurfaceEvaluator::
 set_extended_vertex(int ui, int vi, int d, float value) {
 set_extended_vertex(int ui, int vi, int d, float value) {
   nassertv(ui >= 0 && ui < _num_u_vertices &&
   nassertv(ui >= 0 && ui < _num_u_vertices &&
            vi >= 0 && vi < _num_v_vertices);
            vi >= 0 && vi < _num_v_vertices);
@@ -217,7 +217,7 @@ set_extended_vertex(int ui, int vi, int d, float value) {
 //               for the indicated dimension, or 0.0 if nothing has
 //               for the indicated dimension, or 0.0 if nothing has
 //               been set.
 //               been set.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-float NurbsSurfaceEvaluator::
+INLINE float NurbsSurfaceEvaluator::
 get_extended_vertex(int ui, int vi, int d) const {
 get_extended_vertex(int ui, int vi, int d) const {
   nassertr(ui >= 0 && ui < _num_u_vertices &&
   nassertr(ui >= 0 && ui < _num_u_vertices &&
            vi >= 0 && vi < _num_v_vertices, 0.0f);
            vi >= 0 && vi < _num_v_vertices, 0.0f);
@@ -248,6 +248,36 @@ get_num_v_knots() const {
   return _num_v_vertices + _v_order;
   return _num_v_vertices + _v_order;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsSurfaceEvaluator::get_num_u_segments
+//       Access: Published
+//  Description: Returns the number of piecewise continuous segments
+//               in the surface in the U direction.  This is based on
+//               the knot vector.
+////////////////////////////////////////////////////////////////////
+INLINE int NurbsSurfaceEvaluator::
+get_num_u_segments() const {
+  if (_u_basis_dirty) {
+    ((NurbsSurfaceEvaluator *)this)->recompute_u_basis();
+  }
+  return _u_basis.get_num_segments();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: NurbsSurfaceEvaluator::get_num_v_segments
+//       Access: Published
+//  Description: Returns the number of piecewise continuous segments
+//               in the surface in the V direction.  This is based on
+//               the knot vector.
+////////////////////////////////////////////////////////////////////
+INLINE int NurbsSurfaceEvaluator::
+get_num_v_segments() const {
+  if (_v_basis_dirty) {
+    ((NurbsSurfaceEvaluator *)this)->recompute_v_basis();
+  }
+  return _v_basis.get_num_segments();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NurbsSurfaceEvaluator::vert
 //     Function: NurbsSurfaceEvaluator::vert
 //       Access: Private
 //       Access: Private

+ 2 - 2
panda/src/parametrics/nurbsSurfaceEvaluator.cxx

@@ -309,7 +309,7 @@ recompute_u_basis() {
   _u_basis.clear(_u_order);
   _u_basis.clear(_u_order);
   if (_num_u_vertices > _u_order - 1) {
   if (_num_u_vertices > _u_order - 1) {
     int min_knot = _u_order;
     int min_knot = _u_order;
-    int max_knot = _num_u_vertices + 1;
+    int max_knot = _num_u_vertices;
     
     
     for (int i = min_knot; i <= max_knot; i++) {
     for (int i = min_knot; i <= max_knot; i++) {
       nassertv(i - 1 >= 0 && i < (int)_u_knots.size());
       nassertv(i - 1 >= 0 && i < (int)_u_knots.size());
@@ -338,7 +338,7 @@ recompute_v_basis() {
   _v_basis.clear(_v_order);
   _v_basis.clear(_v_order);
   if (_num_v_vertices > _v_order - 1) {
   if (_num_v_vertices > _v_order - 1) {
     int min_knot = _v_order;
     int min_knot = _v_order;
-    int max_knot = _num_v_vertices + 1;
+    int max_knot = _num_v_vertices;
     
     
     for (int i = min_knot; i <= max_knot; i++) {
     for (int i = min_knot; i <= max_knot; i++) {
       nassertv(i - 1 >= 0 && i < (int)_v_knots.size());
       nassertv(i - 1 >= 0 && i < (int)_v_knots.size());

+ 3 - 0
panda/src/parametrics/nurbsSurfaceEvaluator.h

@@ -72,6 +72,9 @@ PUBLISHED:
   void set_v_knot(int i, float knot);
   void set_v_knot(int i, float knot);
   float get_v_knot(int i) const;
   float get_v_knot(int i) const;
 
 
+  INLINE int get_num_u_segments() const;
+  INLINE int get_num_v_segments() const;
+
   PT(NurbsSurfaceResult) evaluate(const NodePath &rel_to = NodePath()) const;
   PT(NurbsSurfaceResult) evaluate(const NodePath &rel_to = NodePath()) const;
 
 
 public:
 public:

+ 79 - 77
panda/src/parametrics/nurbsSurfaceResult.cxx

@@ -46,63 +46,54 @@ NurbsSurfaceResult(const NurbsBasisVector &u_basis,
   int num_segments = num_u_segments * num_v_segments;
   int num_segments = num_u_segments * num_v_segments;
 
 
   _composed.reserve(num_segments);
   _composed.reserve(num_segments);
-  for (int ui = 0; ui < num_u_segments; ui++) {
-    const LMatrix4f &u_basis_mat = _u_basis.get_basis(ui);
-
-    int un = _u_basis.get_vertex_index(ui);
-    nassertv(un >= 0 && un + u_order - 1 < _num_u_vertices);
-
-    for (int vi = 0; vi < num_v_segments; vi++) {
-      LMatrix4f v_basis_transpose = transpose(_v_basis.get_basis(vi));
-
-      int vn = _v_basis.get_vertex_index(vi);
-      nassertv(vn >= 0 && vn + v_order - 1 < _num_v_vertices);
+  for (int i = 0; i < num_segments; i++) {
+    _composed.push_back(ComposedMats());
+  }
 
 
+  for (int vi = 0; vi < num_v_segments; vi++) {
+    LMatrix4f v_basis_transpose = transpose(_v_basis.get_basis(vi));
+    
+    int vn = _v_basis.get_vertex_index(vi);
+    nassertv(vn >= 0 && vn + v_order - 1 < _num_v_vertices);
+    
+    for (int ui = 0; ui < num_u_segments; ui++) {
+      const LMatrix4f &u_basis_mat = _u_basis.get_basis(ui);
+      
+      int un = _u_basis.get_vertex_index(ui);
+      nassertv(un >= 0 && un + u_order - 1 < _num_u_vertices);
+      
       // Create four geometry matrices from our (up to) sixteen
       // Create four geometry matrices from our (up to) sixteen
       // involved vertices.
       // involved vertices.
-      LVecBase4f c[4][4];
+      LMatrix4f geom_x, geom_y, geom_z, geom_w;
+      memset(&geom_x, 0, sizeof(geom_x));
+      memset(&geom_y, 0, sizeof(geom_y));
+      memset(&geom_z, 0, sizeof(geom_z));
+      memset(&geom_w, 0, sizeof(geom_w));
+
       for (int uni = 0; uni < 4; uni++) {
       for (int uni = 0; uni < 4; uni++) {
         for (int vni = 0; vni < 4; vni++) {
         for (int vni = 0; vni < 4; vni++) {
-          c[uni][vni] = (uni < u_order && vni < v_order) ? 
-            vecs[verti(un + uni, vn + vni)] :
-            LVecBase4f::zero();
+          if (uni < u_order && vni < v_order) {
+            const LVecBase4f &vec = vecs[verti(un + uni, vn + vni)];
+            geom_x(uni, vni) = vec[0];
+            geom_y(uni, vni) = vec[1];
+            geom_z(uni, vni) = vec[2];
+            geom_w(uni, vni) = vec[3];
+          }
         }
         }
       }
       }
 
 
-      LMatrix4f geom_x(c[0][0][0], c[0][1][0], c[0][2][0], c[0][3][0],
-                       c[1][0][0], c[1][1][0], c[1][2][0], c[1][3][0],
-                       c[2][0][0], c[2][1][0], c[2][2][0], c[2][3][0],
-                       c[3][0][0], c[3][1][0], c[3][2][0], c[3][3][0]);
-
-      LMatrix4f geom_y(c[0][0][1], c[0][1][1], c[0][2][1], c[0][3][1],
-                       c[1][0][1], c[1][1][1], c[1][2][1], c[1][3][1],
-                       c[2][0][1], c[2][1][1], c[2][2][1], c[2][3][1],
-                       c[3][0][1], c[3][1][1], c[3][2][1], c[3][3][1]);
-
-      LMatrix4f geom_z(c[0][0][2], c[0][1][2], c[0][2][2], c[0][3][2],
-                       c[1][0][2], c[1][1][2], c[1][2][2], c[1][3][2],
-                       c[2][0][2], c[2][1][2], c[2][2][2], c[2][3][2],
-                       c[3][0][2], c[3][1][2], c[3][2][2], c[3][3][2]);
-      
-      LMatrix4f geom_w(c[0][0][3], c[0][1][3], c[0][2][3], c[0][3][3],
-                       c[1][0][3], c[1][1][3], c[1][2][3], c[1][3][3],
-                       c[2][0][3], c[2][1][3], c[2][2][3], c[2][3][3],
-                       c[3][0][3], c[3][1][3], c[3][2][3], c[3][3][3]);
-
       // And compose these geometry matrices with the basis matrices
       // And compose these geometry matrices with the basis matrices
       // to produce a new set of matrices, which will be used to
       // to produce a new set of matrices, which will be used to
       // evaluate the surface.
       // evaluate the surface.
-      ComposedMats result;
+      int i = segi(ui, vi);
+      nassertv(i >= 0 && i < (int)_composed.size());
+      ComposedMats &result = _composed[i];
       result._x = u_basis_mat * geom_x * v_basis_transpose;
       result._x = u_basis_mat * geom_x * v_basis_transpose;
       result._y = u_basis_mat * geom_y * v_basis_transpose;
       result._y = u_basis_mat * geom_y * v_basis_transpose;
       result._z = u_basis_mat * geom_z * v_basis_transpose;
       result._z = u_basis_mat * geom_z * v_basis_transpose;
       result._w = u_basis_mat * geom_w * v_basis_transpose;
       result._w = u_basis_mat * geom_w * v_basis_transpose;
-
-      _composed.push_back(result);
     }
     }
   }
   }
-
-  nassertv((int)_composed.size() == num_segments);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -125,12 +116,13 @@ NurbsSurfaceResult(const NurbsBasisVector &u_basis,
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NurbsSurfaceResult::
 void NurbsSurfaceResult::
 eval_segment_point(int ui, int vi, float u, float v, LVecBase3f &point) const {
 eval_segment_point(int ui, int vi, float u, float v, LVecBase3f &point) const {
+  int i = segi(ui, vi);
+  nassertv(i >= 0 && i < (int)_composed.size());
+
   float u2 = u*u;
   float u2 = u*u;
   LVecBase4f uvec(u*u2, u2, u, 1.0f);
   LVecBase4f uvec(u*u2, u2, u, 1.0f);
   float v2 = v*v;
   float v2 = v*v;
   LVecBase4f vvec(v*v2, v2, v, 1.0f);
   LVecBase4f vvec(v*v2, v2, v, 1.0f);
-  int i = segi(ui, vi);
-  nassertv(i >= 0 && i < (int)_composed.size());
 
 
   float weight = vvec.dot(uvec * _composed[i]._w);
   float weight = vvec.dot(uvec * _composed[i]._w);
 
 
@@ -149,14 +141,25 @@ eval_segment_point(int ui, int vi, float u, float v, LVecBase3f &point) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void NurbsSurfaceResult::
 void NurbsSurfaceResult::
 eval_segment_normal(int ui, int vi, float u, float v, LVecBase3f &normal) const {
 eval_segment_normal(int ui, int vi, float u, float v, LVecBase3f &normal) const {
-  /*
-  float t2 = t*t;
-  LVecBase4f tvec(t2, t, 1.0f, 0.0f);
-
-  normal.set(tvec.dot(_composed[segment].get_col(0)),
-              tvec.dot(_composed[segment].get_col(1)),
-              tvec.dot(_composed[segment].get_col(2)));
-  */
+  int i = segi(ui, vi);
+  nassertv(i >= 0 && i < (int)_composed.size());
+
+  float u2 = u*u;
+  LVecBase4f uvec(u*u2, u2, u, 1.0f);
+  LVecBase4f duvec(3.0f * u2, 2.0f * u, 1.0f, 0.0f);
+  float v2 = v*v;
+  LVecBase4f vvec(v*v2, v2, v, 1.0f);
+  LVecBase4f dvvec(3.0f * v2, 2.0f * v, 1.0f, 0.0f);
+
+  LVector3f utan(vvec.dot(duvec * _composed[i]._x),
+                 vvec.dot(duvec * _composed[i]._y),
+                 vvec.dot(duvec * _composed[i]._z));
+
+  LVector3f vtan(dvvec.dot(uvec * _composed[i]._x),
+                 dvvec.dot(uvec * _composed[i]._y),
+                 dvvec.dot(uvec * _composed[i]._z));
+
+  normal = utan.cross(vtan);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -168,40 +171,39 @@ eval_segment_normal(int ui, int vi, float u, float v, LVecBase3f &normal) const
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 float NurbsSurfaceResult::
 float NurbsSurfaceResult::
 eval_segment_extended_point(int ui, int vi, float u, float v, int d) const {
 eval_segment_extended_point(int ui, int vi, float u, float v, int d) const {
-  /*
-  nassertr(segment >= 0 && segment < _basis.get_num_segments(), 0.0f);
+  // Calculate the composition of the basis matrices and the geometry
+  // matrix on-the-fly.
+  LMatrix4f v_basis_transpose = transpose(_v_basis.get_basis(vi));
+  const LMatrix4f &u_basis_mat = _u_basis.get_basis(ui);
+  int u_order = _u_basis.get_order();
+  int v_order = _v_basis.get_order();
 
 
-  int order = _basis.get_order();
-  int vi = _basis.get_vertex_index(segment);
+  int un = _u_basis.get_vertex_index(ui);
+  int vn = _v_basis.get_vertex_index(vi);
 
 
-  LVecBase4f geom;
-  int ci = 0;
-  while (ci < order) {
-    geom[ci] = _verts[vi + ci].get_extended_vertex(d);
-    ci++;
-  }
-  while (ci < 4) {
-    geom[ci] = 0.0f;
-    ci++;
-  }
+  LMatrix4f geom;
+  memset(&geom, 0, sizeof(geom));
 
 
-  const LMatrix4f &basis = _basis.get_basis(segment);
+  for (int uni = 0; uni < 4; uni++) {
+    for (int vni = 0; vni < 4; vni++) {
+      if (uni < u_order && vni < v_order) {
+        geom(uni, vni) = _verts[verti(un + uni, vn + vni)].get_extended_vertex(d);
+      }
+    }
+  }
 
 
-  // Compute matrix * column vector.
-  LVecBase4f composed_geom(basis.get_row(0).dot(geom),
-                           basis.get_row(1).dot(geom),
-                           basis.get_row(2).dot(geom),
-                           basis.get_row(3).dot(geom));
+  LMatrix4f composed = u_basis_mat * geom * v_basis_transpose;
 
 
-  float t2 = t*t;
-  LVecBase4f tvec(t*t2, t2, t, 1.0f);
+  int i = segi(ui, vi);
+  nassertr(i >= 0 && i < (int)_composed.size(), 0.0f);
 
 
-  float weight = tvec.dot(_composed[segment].get_col(3));
+  float u2 = u*u;
+  LVecBase4f uvec(u*u2, u2, u, 1.0f);
+  float v2 = v*v;
+  LVecBase4f vvec(v*v2, v2, v, 1.0f);
 
 
-  float result = tvec.dot(composed_geom) / weight;
-  return result;
-  */
-  return 0.0f;
+  float weight = vvec.dot(uvec * _composed[i]._w);
+  return vvec.dot(uvec * composed) / weight;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/parametrics/parametrics_composite2.cxx

@@ -10,3 +10,4 @@
 #include "nurbsBasisVector.cxx"
 #include "nurbsBasisVector.cxx"
 #include "nurbsVertex.cxx"
 #include "nurbsVertex.cxx"
 #include "ropeNode.cxx"
 #include "ropeNode.cxx"
+#include "sheetNode.cxx"

+ 44 - 14
panda/src/parametrics/ropeNode.I

@@ -28,7 +28,8 @@ CData() {
   _render_mode = RopeNode::RM_thread;
   _render_mode = RopeNode::RM_thread;
   _uv_mode = RopeNode::UV_none;
   _uv_mode = RopeNode::UV_none;
   _uv_scale.set(1.0f, 1.0f);
   _uv_scale.set(1.0f, 1.0f);
-  _num_segs = 10;
+  _use_vertex_color = false;
+  _num_subdiv = 10;
   _thickness = 1.0f;
   _thickness = 1.0f;
 }
 }
 
 
@@ -43,7 +44,8 @@ CData(const RopeNode::CData &copy) :
   _render_mode(copy._render_mode),
   _render_mode(copy._render_mode),
   _uv_mode(copy._uv_mode),
   _uv_mode(copy._uv_mode),
   _uv_scale(copy._uv_scale),
   _uv_scale(copy._uv_scale),
-  _num_segs(copy._num_segs),
+  _use_vertex_color(copy._use_vertex_color),
+  _num_subdiv(copy._num_subdiv),
   _thickness(copy._thickness)
   _thickness(copy._thickness)
 {
 {
 }
 }
@@ -145,29 +147,57 @@ get_uv_scale() const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: set_num_segs
+//     Function: set_use_vertex_color
 //       Access: Public
 //       Access: Public
-//  Description: Specifies the number of segments per cubic segment
-//               (that is, per unique knot value) to draw in a fixed
-//               uniform tesselation of the curve.
+//  Description: Sets the "use vertex color" flag.  When this is true,
+//               the R, G, B, A vertex color is assumed to be stored
+//               as the dimensions 0, 1, 2, 3, respectively, of the
+//               extended vertex values.  Use
+//               NurbsCurveEvaluator::set_extended_vertex() to set
+//               these values.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void RopeNode::
 INLINE void RopeNode::
-set_num_segs(int num_segs) {
-  nassertv(num_segs >= 0);
+set_use_vertex_color(bool flag) {
   CDWriter cdata(_cycler);
   CDWriter cdata(_cycler);
-  cdata->_num_segs = num_segs;
+  cdata->_use_vertex_color = flag;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: get_num_segs
+//     Function: get_use_vertex_color
 //       Access: Public
 //       Access: Public
-//  Description: Returns the number of segments per cubic segment to
-//               draw.  See set_num_segs().
+//  Description: Returns the "use vertex color" flag.  See
+//               set_use_vertex_color().
+////////////////////////////////////////////////////////////////////
+INLINE bool RopeNode::
+get_use_vertex_color() const {
+  CDReader cdata(_cycler);
+  return cdata->_use_vertex_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: set_num_subdiv
+//       Access: Public
+//  Description: Specifies the number of subdivisions per cubic
+//               segment (that is, per unique knot value) to draw in a
+//               fixed uniform tesselation of the curve.
+////////////////////////////////////////////////////////////////////
+INLINE void RopeNode::
+set_num_subdiv(int num_subdiv) {
+  nassertv(num_subdiv >= 0);
+  CDWriter cdata(_cycler);
+  cdata->_num_subdiv = num_subdiv;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_num_subdiv
+//       Access: Public
+//  Description: Returns the number of subdivisions per cubic segment
+//               to draw.  See set_num_subdiv().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int RopeNode::
 INLINE int RopeNode::
-get_num_segs() const {
+get_num_subdiv() const {
   CDReader cdata(_cycler);
   CDReader cdata(_cycler);
-  return cdata->_num_segs;
+  return cdata->_num_subdiv;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 32 - 17
panda/src/parametrics/ropeNode.cxx

@@ -151,19 +151,21 @@ has_cull_callback() const {
 bool RopeNode::
 bool RopeNode::
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
 cull_callback(CullTraverser *trav, CullTraverserData &data) {
   // Create some geometry on-the-fly to render the rope.
   // Create some geometry on-the-fly to render the rope.
-  if (get_num_segs() > 0) {
+  if (get_num_subdiv() > 0) {
     NurbsCurveEvaluator *curve = get_curve();
     NurbsCurveEvaluator *curve = get_curve();
-    PT(NurbsCurveResult) result = curve->evaluate(data._node_path.get_node_path());
-
-    if (result->get_num_segments() > 0) {
-      switch (get_render_mode()) {
-      case RM_thread:
-        render_thread(trav, data, result);
-        break;
-
-      case RM_billboard:
-        render_billboard(trav, data, result);
-        break;
+    if (curve != (NurbsCurveEvaluator *)NULL) {
+      PT(NurbsCurveResult) result = curve->evaluate(data._node_path.get_node_path());
+
+      if (result->get_num_segments() > 0) {
+        switch (get_render_mode()) {
+        case RM_thread:
+          render_thread(trav, data, result);
+          break;
+          
+        case RM_billboard:
+          render_billboard(trav, data, result);
+          break;
+        }
       }
       }
     }
     }
   }
   }
@@ -261,13 +263,14 @@ render_thread(CullTraverser *trav, CullTraverserData &data,
               NurbsCurveResult *result) {
               NurbsCurveResult *result) {
   UVMode uv_mode = get_uv_mode();
   UVMode uv_mode = get_uv_mode();
   LVecBase2f uv_scale = get_uv_scale();
   LVecBase2f uv_scale = get_uv_scale();
+  bool use_vertex_color = get_use_vertex_color();
 
 
   PTA_Vertexf verts;
   PTA_Vertexf verts;
   PTA_TexCoordf uvs;
   PTA_TexCoordf uvs;
   PTA_Colorf colors;
   PTA_Colorf colors;
   PTA_int lengths;
   PTA_int lengths;
 
 
-  int num_verts = get_num_segs() + 1;
+  int num_verts = get_num_subdiv() + 1;
   int num_segments = result->get_num_segments();
   int num_segments = result->get_num_segments();
   float dist = 0.0f;
   float dist = 0.0f;
   for (int segment = 0; segment < num_segments; segment++) {
   for (int segment = 0; segment < num_segments; segment++) {
@@ -278,6 +281,14 @@ render_thread(CullTraverser *trav, CullTraverserData &data,
       result->eval_segment_point(segment, t, point);
       result->eval_segment_point(segment, t, point);
       verts.push_back(point);
       verts.push_back(point);
 
 
+      if (use_vertex_color) {
+        Colorf color(result->eval_segment_extended_point(segment, t, 0),
+                     result->eval_segment_extended_point(segment, t, 1),
+                     result->eval_segment_extended_point(segment, t, 2),
+                     result->eval_segment_extended_point(segment, t, 3));
+        colors.push_back(color);
+      }
+
       t = result->get_segment_t(segment, t);
       t = result->get_segment_t(segment, t);
       switch (uv_mode) {
       switch (uv_mode) {
       case UV_none:
       case UV_none:
@@ -308,8 +319,6 @@ render_thread(CullTraverser *trav, CullTraverserData &data,
     }
     }
     lengths.push_back(num_verts);
     lengths.push_back(num_verts);
   }
   }
-
-  colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
   
   
   PT(GeomLinestrip) geom = new GeomLinestrip;
   PT(GeomLinestrip) geom = new GeomLinestrip;
   geom->set_width(get_thickness());
   geom->set_width(get_thickness());
@@ -318,7 +327,13 @@ render_thread(CullTraverser *trav, CullTraverserData &data,
   if (uv_mode != UV_none) {
   if (uv_mode != UV_none) {
     geom->set_texcoords(uvs, G_PER_VERTEX);
     geom->set_texcoords(uvs, G_PER_VERTEX);
   }
   }
-  geom->set_colors(colors, G_OVERALL);
+
+  if (use_vertex_color) {
+    geom->set_colors(colors, G_PER_VERTEX);
+  } else {
+    colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
+    geom->set_colors(colors, G_OVERALL);
+  }
   geom->set_lengths(lengths);
   geom->set_lengths(lengths);
   
   
   CullableObject *object = new CullableObject(geom, data._state,
   CullableObject *object = new CullableObject(geom, data._state,
@@ -355,7 +370,7 @@ render_billboard(CullTraverser *trav, CullTraverserData &data,
   // build one continuous tristrip for all connected segments, so we
   // build one continuous tristrip for all connected segments, so we
   // can stitch them together properly at the seams.
   // can stitch them together properly at the seams.
 
 
-  int num_verts = get_num_segs() + 1;
+  int num_verts = get_num_subdiv() + 1;
   int num_segments = result->get_num_segments();
   int num_segments = result->get_num_segments();
 
 
   vector_Vertexf center_verts;
   vector_Vertexf center_verts;

+ 7 - 3
panda/src/parametrics/ropeNode.h

@@ -95,8 +95,11 @@ PUBLISHED:
   INLINE void set_uv_scale(const LVecBase2f &uv_scale);
   INLINE void set_uv_scale(const LVecBase2f &uv_scale);
   INLINE const LVecBase2f &get_uv_scale() const;
   INLINE const LVecBase2f &get_uv_scale() const;
 
 
-  INLINE void set_num_segs(int num_segs);
-  INLINE int get_num_segs() const;
+  INLINE void set_use_vertex_color(bool flag);
+  INLINE bool get_use_vertex_color() const;
+
+  INLINE void set_num_subdiv(int num_subdiv);
+  INLINE int get_num_subdiv() const;
 
 
   INLINE void set_thickness(float thickness);
   INLINE void set_thickness(float thickness);
   INLINE float get_thickness() const;
   INLINE float get_thickness() const;
@@ -127,7 +130,8 @@ private:
     RenderMode _render_mode;
     RenderMode _render_mode;
     UVMode _uv_mode;
     UVMode _uv_mode;
     LVecBase2f _uv_scale;
     LVecBase2f _uv_scale;
-    int _num_segs;
+    bool _use_vertex_color;
+    int _num_subdiv;
     float _thickness;
     float _thickness;
   };
   };
 
 

+ 150 - 0
panda/src/parametrics/sheetNode.I

@@ -0,0 +1,150 @@
+// Filename: sheetNode.I
+// Created by:  drose (11Oct03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::CData::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SheetNode::CData::
+CData() {
+  _surface = new NurbsSurfaceEvaluator;
+  _use_vertex_color = false;
+  _num_u_subdiv = 2;
+  _num_v_subdiv = 2;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::CData::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SheetNode::CData::
+CData(const SheetNode::CData &copy) :
+  _surface(copy._surface),
+  _use_vertex_color(copy._use_vertex_color),
+  _num_u_subdiv(copy._num_u_subdiv),
+  _num_v_subdiv(copy._num_v_subdiv)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: set_surface
+//       Access: Public
+//  Description: Sets the particular surface represented by the
+//               SheetNode.
+////////////////////////////////////////////////////////////////////
+INLINE void SheetNode::
+set_surface(NurbsSurfaceEvaluator *surface) {
+  CDWriter cdata(_cycler);
+  cdata->_surface = surface;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_surface
+//       Access: Public
+//  Description: Returns the surface represented by the SheetNode.
+////////////////////////////////////////////////////////////////////
+INLINE NurbsSurfaceEvaluator *SheetNode::
+get_surface() const {
+  CDReader cdata(_cycler);
+  return cdata->_surface;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: set_use_vertex_color
+//       Access: Public
+//  Description: Sets the "use vertex color" flag.  When this is true,
+//               the R, G, B, A vertex color is assumed to be stored
+//               as the dimensions 0, 1, 2, 3, respectively, of the
+//               extended vertex values.  Use
+//               NurbsCurveEvaluator::set_extended_vertex() to set
+//               these values.
+////////////////////////////////////////////////////////////////////
+INLINE void SheetNode::
+set_use_vertex_color(bool flag) {
+  CDWriter cdata(_cycler);
+  cdata->_use_vertex_color = flag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_use_vertex_color
+//       Access: Public
+//  Description: Returns the "use vertex color" flag.  See
+//               set_use_vertex_color().
+////////////////////////////////////////////////////////////////////
+INLINE bool SheetNode::
+get_use_vertex_color() const {
+  CDReader cdata(_cycler);
+  return cdata->_use_vertex_color;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: set_num_u_subdiv
+//       Access: Public
+//  Description: Specifies the number of subdivisions per cubic
+//               segment (that is, per unique knot value) to draw in a
+//               fixed uniform tesselation of the surface in the U
+//               direction.
+////////////////////////////////////////////////////////////////////
+INLINE void SheetNode::
+set_num_u_subdiv(int num_u_subdiv) {
+  nassertv(num_u_subdiv >= 0);
+  CDWriter cdata(_cycler);
+  cdata->_num_u_subdiv = num_u_subdiv;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_num_u_subdiv
+//       Access: Public
+//  Description: Returns the number of subdivisions per cubic segment
+//               to draw in the U direction.  See set_num_u_subdiv().
+////////////////////////////////////////////////////////////////////
+INLINE int SheetNode::
+get_num_u_subdiv() const {
+  CDReader cdata(_cycler);
+  return cdata->_num_u_subdiv;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: set_num_v_subdiv
+//       Access: Public
+//  Description: Specifies the number of subdivisions per cubic
+//               segment (that is, per unique knot value) to draw in a
+//               fixed uniform tesselation of the surface in the V
+//               direction.
+////////////////////////////////////////////////////////////////////
+INLINE void SheetNode::
+set_num_v_subdiv(int num_v_subdiv) {
+  nassertv(num_v_subdiv >= 0);
+  CDWriter cdata(_cycler);
+  cdata->_num_v_subdiv = num_v_subdiv;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: get_num_v_subdiv
+//       Access: Public
+//  Description: Returns the number of subdivisions per cubic segment
+//               to draw in the V direction.  See set_num_v_subdiv().
+////////////////////////////////////////////////////////////////////
+INLINE int SheetNode::
+get_num_v_subdiv() const {
+  CDReader cdata(_cycler);
+  return cdata->_num_v_subdiv;
+}

+ 383 - 0
panda/src/parametrics/sheetNode.cxx

@@ -0,0 +1,383 @@
+// Filename: sheetNode.cxx
+// Created by:  drose (11Oct03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "sheetNode.h"
+#include "cullTraverser.h"
+#include "cullTraverserData.h"
+#include "cullableObject.h"
+#include "cullHandler.h"
+#include "geomTristrip.h"
+#include "bamWriter.h"
+#include "bamReader.h"
+#include "datagram.h"
+#include "datagramIterator.h"
+
+TypeHandle SheetNode::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::CData::make_copy
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CycleData *SheetNode::CData::
+make_copy() const {
+  return new CData(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::CData::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void SheetNode::CData::
+write_datagram(BamWriter *writer, Datagram &dg) const {
+  // For now, we write a NULL pointer.  Eventually we will write out
+  // the NurbsSurfaceEvaluator pointer.
+  writer->write_pointer(dg, (TypedWritable *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::CData::fillin
+//       Access: Public, Virtual
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new SheetNode.
+////////////////////////////////////////////////////////////////////
+void SheetNode::CData::
+fillin(DatagramIterator &scan, BamReader *reader) {
+  // For now, we skip over the NULL pointer that we wrote out.
+  reader->skip_pointer(scan);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+SheetNode::
+SheetNode(const string &name) :
+  PandaNode(name)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::Copy Constructor
+//       Access: Protected
+//  Description:
+////////////////////////////////////////////////////////////////////
+SheetNode::
+SheetNode(const SheetNode &copy) :
+  PandaNode(copy),
+  _cycler(copy._cycler)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::make_copy
+//       Access: Public, Virtual
+//  Description: Returns a newly-allocated Node that is a shallow copy
+//               of this one.  It will be a different Node pointer,
+//               but its internal data may or may not be shared with
+//               that of the original Node.
+////////////////////////////////////////////////////////////////////
+PandaNode *SheetNode::
+make_copy() const {
+  return new SheetNode(*this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::safe_to_transform
+//       Access: Public, Virtual
+//  Description: Returns true if it is generally safe to transform
+//               this particular kind of Node by calling the xform()
+//               method, false otherwise.  For instance, it's usually
+//               a bad idea to attempt to xform a SheetNode.
+////////////////////////////////////////////////////////////////////
+bool SheetNode::
+safe_to_transform() const {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::has_cull_callback
+//       Access: Public, Virtual
+//  Description: Should be overridden by derived classes to return
+//               true if cull_callback() has been defined.  Otherwise,
+//               returns false to indicate cull_callback() does not
+//               need to be called for this node during the cull
+//               traversal.
+////////////////////////////////////////////////////////////////////
+bool SheetNode::
+has_cull_callback() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::cull_callback
+//       Access: Public, Virtual
+//  Description: If has_cull_callback() returns true, this function
+//               will be called during the cull traversal to perform
+//               any additional operations that should be performed at
+//               cull time.  This may include additional manipulation
+//               of render state or additional visible/invisible
+//               decisions, or any other arbitrary operation.
+//
+//               By the time this function is called, the node has
+//               already passed the bounding-volume test for the
+//               viewing frustum, and the node's transform and state
+//               have already been applied to the indicated
+//               CullTraverserData object.
+//
+//               The return value is true if this node should be
+//               visible, or false if it should be culled.
+////////////////////////////////////////////////////////////////////
+bool SheetNode::
+cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  // Create some geometry on-the-fly to render the sheet.
+  if (get_num_u_subdiv() > 0 && get_num_v_subdiv() > 0) {
+    NurbsSurfaceEvaluator *surface = get_surface();
+    if (surface != (NurbsSurfaceEvaluator *)NULL) {
+      PT(NurbsSurfaceResult) result = surface->evaluate(data._node_path.get_node_path());
+      
+      if (result->get_num_u_segments() > 0 && result->get_num_v_segments() > 0) {
+        render_sheet(trav, data, result);
+      }
+    }
+  }
+  
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::output
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SheetNode::
+output(ostream &out) const {
+  PandaNode::output(out);
+  out << " " << get_surface();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::write
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SheetNode::
+write(ostream &out, int indent_level) const {
+  PandaNode::write(out, indent_level);
+  indent(out, indent_level) << get_surface() << "\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::reset_bound
+//       Access: Published
+//  Description: Recomputes the bounding volume.  This is normally
+//               called automatically, but it must occasionally be
+//               called explicitly when the surface has changed
+//               properties outside of this node's knowledge.
+////////////////////////////////////////////////////////////////////
+void SheetNode::
+reset_bound(const NodePath &rel_to) {
+  do_recompute_bound(rel_to);
+  changed_internal_bound();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::recompute_internal_bound
+//       Access: Protected, Virtual
+//  Description: Called when needed to recompute the node's
+//               _internal_bound object.  Nodes that contain anything
+//               of substance should redefine this to do the right
+//               thing.
+////////////////////////////////////////////////////////////////////
+BoundingVolume *SheetNode::
+recompute_internal_bound() {
+  return do_recompute_bound(NodePath(this));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::do_recompute_bound
+//       Access: Private
+//  Description: Does the actual internal recompute.
+////////////////////////////////////////////////////////////////////
+BoundingVolume *SheetNode::
+do_recompute_bound(const NodePath &rel_to) {
+  // First, get ourselves a fresh, empty bounding volume.
+  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  nassertr(bound != (BoundingVolume *)NULL, bound);
+  
+  NurbsSurfaceEvaluator *surface = get_surface();
+  if (surface != (NurbsSurfaceEvaluator *)NULL) {
+    pvector<LPoint3f> verts;
+    get_surface()->get_vertices(verts, rel_to);
+    
+    GeometricBoundingVolume *gbv;
+    DCAST_INTO_R(gbv, bound, bound);
+    gbv->around(&verts[0], &verts[verts.size() - 1]);
+  }
+  return bound;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::render_sheet
+//       Access: Private
+//  Description: Draws the sheet as a series of tristrips along its
+//               length.
+////////////////////////////////////////////////////////////////////
+void SheetNode::
+render_sheet(CullTraverser *trav, CullTraverserData &data, 
+              NurbsSurfaceResult *result) {
+  bool use_vertex_color = get_use_vertex_color();
+
+  PTA_Vertexf verts;
+  PTA_Normalf normals;
+  PTA_TexCoordf uvs;
+  PTA_Colorf colors;
+  PTA_int lengths;
+
+  // We define a series of triangle strips, parallel to the V
+  // direction.
+
+  int num_u_segments = result->get_num_u_segments();
+  int num_v_segments = result->get_num_v_segments();
+  int num_u_verts = get_num_u_subdiv() + 1;
+  int num_v_verts = get_num_v_subdiv() + 1;
+
+  for (int ui = 0; ui < num_u_segments; ui++) {
+    for (int uni = 0; uni < num_u_verts; uni++) {
+      float u0 = (float)uni / (float)num_u_verts;
+      float u1 = (float)(uni + 1) / (float)num_u_verts;
+      float u0_tc = result->get_segment_u(ui, u0);
+      float u1_tc = result->get_segment_u(ui, u1);
+
+      for (int vi = 0; vi < num_v_segments; vi++) {
+        for (int vni = 0; vni < num_v_verts; vni++) {
+          float v = (float)vni / (float)(num_v_verts - 1);
+          float v_tc = result->get_segment_v(vi, v);
+
+          LPoint3f point;
+          LVector3f normal;
+          result->eval_segment_point(ui, vi, u0, v, point);
+          result->eval_segment_normal(ui, vi, u0, v, normal);
+          verts.push_back(point);
+          normals.push_back(normal);
+          uvs.push_back(TexCoordf(u0_tc, v_tc));
+
+          result->eval_segment_point(ui, vi, u1, v, point);
+          result->eval_segment_normal(ui, vi, u1, v, normal);
+          verts.push_back(point);
+          normals.push_back(normal);
+          uvs.push_back(TexCoordf(u1_tc, v_tc));
+
+          if (use_vertex_color) {
+            Colorf c0(result->eval_segment_extended_point(ui, vi, u0, v, 0),
+                      result->eval_segment_extended_point(ui, vi, u0, v, 1),
+                      result->eval_segment_extended_point(ui, vi, u0, v, 2),
+                      result->eval_segment_extended_point(ui, vi, u0, v, 3));
+            colors.push_back(c0);
+            Colorf c1(result->eval_segment_extended_point(ui, vi, u1, v, 0),
+                      result->eval_segment_extended_point(ui, vi, u1, v, 1),
+                      result->eval_segment_extended_point(ui, vi, u1, v, 2),
+                      result->eval_segment_extended_point(ui, vi, u1, v, 3));
+            colors.push_back(c1);
+          }
+        }
+        lengths.push_back(num_v_verts * 2);
+      }
+    }
+  }
+
+  colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
+  
+  PT(GeomTristrip) geom = new GeomTristrip;
+  geom->set_num_prims(lengths.size());
+  geom->set_coords(verts);
+  geom->set_normals(normals, G_PER_VERTEX);
+  geom->set_texcoords(uvs, G_PER_VERTEX);
+
+  if (use_vertex_color) {
+    geom->set_colors(colors, G_PER_VERTEX);
+  } else {
+    colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
+    geom->set_colors(colors, G_OVERALL);
+  }
+  geom->set_lengths(lengths);
+  
+  CullableObject *object = new CullableObject(geom, data._state,
+                                              data._render_transform);
+  trav->get_cull_handler()->record_object(object);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::register_with_read_factory
+//       Access: Public, Static
+//  Description: Tells the BamReader how to create objects of type
+//               SheetNode.
+////////////////////////////////////////////////////////////////////
+void SheetNode::
+register_with_read_factory() {
+  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::write_datagram
+//       Access: Public, Virtual
+//  Description: Writes the contents of this object to the datagram
+//               for shipping out to a Bam file.
+////////////////////////////////////////////////////////////////////
+void SheetNode::
+write_datagram(BamWriter *manager, Datagram &dg) {
+  PandaNode::write_datagram(manager, dg);
+  manager->write_cdata(dg, _cycler);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::make_from_bam
+//       Access: Protected, Static
+//  Description: This function is called by the BamReader's factory
+//               when a new object of type SheetNode is encountered
+//               in the Bam file.  It should create the SheetNode
+//               and extract its information from the file.
+////////////////////////////////////////////////////////////////////
+TypedWritable *SheetNode::
+make_from_bam(const FactoryParams &params) {
+  SheetNode *node = new SheetNode("");
+  DatagramIterator scan;
+  BamReader *manager;
+
+  parse_params(params, scan, manager);
+  node->fillin(scan, manager);
+
+  return node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SheetNode::fillin
+//       Access: Protected
+//  Description: This internal function is called by make_from_bam to
+//               read in all of the relevant data from the BamFile for
+//               the new SheetNode.
+////////////////////////////////////////////////////////////////////
+void SheetNode::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  PandaNode::fillin(scan, manager);
+  manager->read_cdata(scan, _cycler);
+}

+ 125 - 0
panda/src/parametrics/sheetNode.h

@@ -0,0 +1,125 @@
+// Filename: sheetNode.h
+// Created by:  drose (11Oct03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef SHEETNODE_H
+#define SHEETNODE_H
+
+#include "pandabase.h"
+#include "nurbsSurfaceEvaluator.h"
+#include "pandaNode.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : SheetNode
+// Description : This class draws a visible representation of the
+//               NURBS surface stored in its NurbsSurfaceEvaluator.  It
+//               automatically recomputes the surface every frame.
+//
+//               This is not related to NurbsSurface, ClassicNurbsSurface,
+//               CubicSurfaceseg or any of the ParametricSurface-derived
+//               objects in this module.  It is a completely parallel
+//               implementation of NURBS surfaces, and will probably
+//               eventually replace the whole ParametricSurface class
+//               hierarchy.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA SheetNode : public PandaNode {
+PUBLISHED:
+  SheetNode(const string &name);
+
+protected:
+  SheetNode(const SheetNode &copy);
+public:
+  virtual void output(ostream &out) const;
+  virtual void write(ostream &out, int indent_level = 0) const;
+
+  virtual PandaNode *make_copy() const;
+
+  virtual bool safe_to_transform() const;
+  virtual bool has_cull_callback() const;
+  virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+
+PUBLISHED:
+  INLINE void set_surface(NurbsSurfaceEvaluator *surface);
+  INLINE NurbsSurfaceEvaluator *get_surface() const;
+
+  INLINE void set_use_vertex_color(bool flag);
+  INLINE bool get_use_vertex_color() const;
+
+  INLINE void set_num_u_subdiv(int num_u_subdiv);
+  INLINE int get_num_u_subdiv() const;
+  INLINE void set_num_v_subdiv(int num_v_subdiv);
+  INLINE int get_num_v_subdiv() const;
+
+  void reset_bound(const NodePath &rel_to);
+
+protected:
+  virtual BoundingVolume *recompute_internal_bound();
+
+private:
+  BoundingVolume *do_recompute_bound(const NodePath &rel_to);
+  void render_sheet(CullTraverser *trav, CullTraverserData &data, 
+                    NurbsSurfaceResult *result);
+
+private:
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA CData : public CycleData {
+  public:
+    INLINE CData();
+    INLINE CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
+    virtual void fillin(DatagramIterator &scan, BamReader *manager);
+
+    PT(NurbsSurfaceEvaluator) _surface;
+    bool _use_vertex_color;
+    int _num_u_subdiv;
+    int _num_v_subdiv;
+  };
+
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *manager, Datagram &dg);
+
+protected:
+  static TypedWritable *make_from_bam(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    PandaNode::init_type();
+    register_type(_type_handle, "SheetNode",
+                  PandaNode::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "sheetNode.I"
+
+#endif