Panda3D

ropeNode.cxx

00001 // Filename: ropeNode.cxx
00002 // Created by:  drose (04Dec02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "ropeNode.h"
00016 #include "cullTraverser.h"
00017 #include "cullTraverserData.h"
00018 #include "cullableObject.h"
00019 #include "cullHandler.h"
00020 #include "renderState.h"
00021 #include "renderModeAttrib.h"
00022 #include "colorAttrib.h"
00023 #include "bamWriter.h"
00024 #include "bamReader.h"
00025 #include "datagram.h"
00026 #include "datagramIterator.h"
00027 #include "pStatTimer.h"
00028 #include "geom.h"
00029 #include "geomLinestrips.h"
00030 #include "geomTristrips.h"
00031 #include "geomVertexWriter.h"
00032 #include "boundingSphere.h"
00033 
00034 TypeHandle RopeNode::_type_handle;
00035 
00036 PStatCollector RopeNode::_rope_node_pcollector("*:RopeNode");
00037 
00038 ////////////////////////////////////////////////////////////////////
00039 //     Function: RopeNode::CData::make_copy
00040 //       Access: Public, Virtual
00041 //  Description:
00042 ////////////////////////////////////////////////////////////////////
00043 CycleData *RopeNode::CData::
00044 make_copy() const {
00045   return new CData(*this);
00046 }
00047 
00048 ////////////////////////////////////////////////////////////////////
00049 //     Function: RopeNode::CData::write_datagram
00050 //       Access: Public, Virtual
00051 //  Description: Writes the contents of this object to the datagram
00052 //               for shipping out to a Bam file.
00053 ////////////////////////////////////////////////////////////////////
00054 void RopeNode::CData::
00055 write_datagram(BamWriter *writer, Datagram &dg) const {
00056   // For now, we write a NULL pointer.  Eventually we will write out
00057   // the NurbsCurveEvaluator pointer.
00058   writer->write_pointer(dg, (TypedWritable *)NULL);
00059 }
00060 
00061 ////////////////////////////////////////////////////////////////////
00062 //     Function: RopeNode::CData::fillin
00063 //       Access: Public, Virtual
00064 //  Description: This internal function is called by make_from_bam to
00065 //               read in all of the relevant data from the BamFile for
00066 //               the new RopeNode.
00067 ////////////////////////////////////////////////////////////////////
00068 void RopeNode::CData::
00069 fillin(DatagramIterator &scan, BamReader *reader) {
00070   // For now, we skip over the NULL pointer that we wrote out.
00071   reader->skip_pointer(scan);
00072 }
00073 
00074 ////////////////////////////////////////////////////////////////////
00075 //     Function: RopeNode::Constructor
00076 //       Access: Public
00077 //  Description:
00078 ////////////////////////////////////////////////////////////////////
00079 RopeNode::
00080 RopeNode(const string &name) :
00081   PandaNode(name)
00082 {
00083   set_cull_callback();
00084 }
00085 
00086 ////////////////////////////////////////////////////////////////////
00087 //     Function: RopeNode::Copy Constructor
00088 //       Access: Protected
00089 //  Description:
00090 ////////////////////////////////////////////////////////////////////
00091 RopeNode::
00092 RopeNode(const RopeNode &copy) :
00093   PandaNode(copy),
00094   _cycler(copy._cycler)
00095 {
00096 }
00097 
00098 ////////////////////////////////////////////////////////////////////
00099 //     Function: RopeNode::make_copy
00100 //       Access: Public, Virtual
00101 //  Description: Returns a newly-allocated Node that is a shallow copy
00102 //               of this one.  It will be a different Node pointer,
00103 //               but its internal data may or may not be shared with
00104 //               that of the original Node.
00105 ////////////////////////////////////////////////////////////////////
00106 PandaNode *RopeNode::
00107 make_copy() const {
00108   return new RopeNode(*this);
00109 }
00110 
00111 ////////////////////////////////////////////////////////////////////
00112 //     Function: RopeNode::safe_to_transform
00113 //       Access: Public, Virtual
00114 //  Description: Returns true if it is generally safe to transform
00115 //               this particular kind of Node by calling the xform()
00116 //               method, false otherwise.  For instance, it's usually
00117 //               a bad idea to attempt to xform a RopeNode.
00118 ////////////////////////////////////////////////////////////////////
00119 bool RopeNode::
00120 safe_to_transform() const {
00121   return false;
00122 }
00123 
00124 ////////////////////////////////////////////////////////////////////
00125 //     Function: RopeNode::cull_callback
00126 //       Access: Public, Virtual
00127 //  Description: This function will be called during the cull
00128 //               traversal to perform any additional operations that
00129 //               should be performed at cull time.  This may include
00130 //               additional manipulation of render state or additional
00131 //               visible/invisible decisions, or any other arbitrary
00132 //               operation.
00133 //
00134 //               Note that this function will *not* be called unless
00135 //               set_cull_callback() is called in the constructor of
00136 //               the derived class.  It is necessary to call
00137 //               set_cull_callback() to indicated that we require
00138 //               cull_callback() to be called.
00139 //
00140 //               By the time this function is called, the node has
00141 //               already passed the bounding-volume test for the
00142 //               viewing frustum, and the node's transform and state
00143 //               have already been applied to the indicated
00144 //               CullTraverserData object.
00145 //
00146 //               The return value is true if this node should be
00147 //               visible, or false if it should be culled.
00148 ////////////////////////////////////////////////////////////////////
00149 bool RopeNode::
00150 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00151   // Statistics
00152   PStatTimer timer(_rope_node_pcollector);
00153 
00154   // Create some geometry on-the-fly to render the rope.
00155   if (get_num_subdiv() > 0) {
00156     NurbsCurveEvaluator *curve = get_curve();
00157     if (curve != (NurbsCurveEvaluator *)NULL) {
00158       PT(NurbsCurveResult) result;
00159       if (has_matrix()) {
00160         result = curve->evaluate(data._node_path.get_node_path(), get_matrix());
00161       } else {
00162         result = curve->evaluate(data._node_path.get_node_path());
00163       }
00164 
00165       if (result->get_num_segments() > 0) {
00166         switch (get_render_mode()) {
00167         case RM_thread:
00168           render_thread(trav, data, result);
00169           break;
00170           
00171         case RM_tape:
00172           render_tape(trav, data, result);
00173           break;
00174           
00175         case RM_billboard:
00176           render_billboard(trav, data, result);
00177           break;
00178           
00179         case RM_tube:
00180           render_tube(trav, data, result);
00181           break;
00182         }
00183       }
00184     }
00185   }
00186   
00187   return true;
00188 }
00189 
00190 ////////////////////////////////////////////////////////////////////
00191 //     Function: RopeNode::is_renderable
00192 //       Access: Public, Virtual
00193 //  Description: Returns true if there is some value to visiting this
00194 //               particular node during the cull traversal for any
00195 //               camera, false otherwise.  This will be used to
00196 //               optimize the result of get_net_draw_show_mask(), so
00197 //               that any subtrees that contain only nodes for which
00198 //               is_renderable() is false need not be visited.
00199 ////////////////////////////////////////////////////////////////////
00200 bool RopeNode::
00201 is_renderable() const {
00202   return true;
00203 }
00204 
00205 ////////////////////////////////////////////////////////////////////
00206 //     Function: RopeNode::output
00207 //       Access: Public, Virtual
00208 //  Description: 
00209 ////////////////////////////////////////////////////////////////////
00210 void RopeNode::
00211 output(ostream &out) const {
00212   PandaNode::output(out);
00213   NurbsCurveEvaluator *curve = get_curve();
00214   if (curve != (NurbsCurveEvaluator *)NULL) {
00215     out << " " << *curve;
00216   } else {
00217     out << " (no curve)";
00218   }
00219 }
00220 
00221 ////////////////////////////////////////////////////////////////////
00222 //     Function: RopeNode::write
00223 //       Access: Public, Virtual
00224 //  Description: 
00225 ////////////////////////////////////////////////////////////////////
00226 void RopeNode::
00227 write(ostream &out, int indent_level) const {
00228   PandaNode::write(out, indent_level);
00229   indent(out, indent_level) << *get_curve() << "\n";
00230 }
00231 
00232 ////////////////////////////////////////////////////////////////////
00233 //     Function: RopeNode::reset_bound
00234 //       Access: Published
00235 //  Description: Recomputes the bounding volume.  This is normally
00236 //               called automatically, but it must occasionally be
00237 //               called explicitly when the curve has changed
00238 //               properties outside of this node's knowledge.
00239 ////////////////////////////////////////////////////////////////////
00240 void RopeNode::
00241 reset_bound(const NodePath &rel_to) {
00242   Thread *current_thread = Thread::get_current_thread();
00243   int pipeline_stage = current_thread->get_pipeline_stage();
00244   do_recompute_bounds(rel_to, pipeline_stage, current_thread);
00245   mark_internal_bounds_stale(current_thread);
00246 }
00247 
00248 ////////////////////////////////////////////////////////////////////
00249 //     Function: RopeNode::compute_internal_bounds
00250 //       Access: Protected, Virtual
00251 //  Description: Called when needed to recompute the node's
00252 //               _internal_bound object.  Nodes that contain anything
00253 //               of substance should redefine this to do the right
00254 //               thing.
00255 ////////////////////////////////////////////////////////////////////
00256 void RopeNode::
00257 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
00258                         int &internal_vertices,
00259                         int pipeline_stage,
00260                         Thread *current_thread) const {
00261   PT(BoundingVolume) bounds = 
00262     do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 
00263                         current_thread);
00264 
00265   internal_bounds = bounds;
00266   internal_vertices = 0;  // TODO--estimate this better.
00267 }
00268 
00269 ////////////////////////////////////////////////////////////////////
00270 //     Function: RopeNode::get_format
00271 //       Access: Private
00272 //  Description: Returns the appropriate GeomVertexFormat for
00273 //               rendering, according to the user-specified
00274 //               requirements.
00275 ////////////////////////////////////////////////////////////////////
00276 CPT(GeomVertexFormat) RopeNode::
00277 get_format(bool support_normals) const {
00278   PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat
00279     (InternalName::get_vertex(), 3, Geom::NT_float32,
00280      Geom::C_point);
00281 
00282   if (support_normals && get_normal_mode() == NM_vertex) {
00283     array_format->add_column
00284       (InternalName::get_normal(), 3, Geom::NT_float32,
00285        Geom::C_vector);
00286   }
00287   if (get_use_vertex_color()) {
00288     array_format->add_column
00289       (InternalName::get_color(), 1, Geom::NT_packed_dabc,
00290        Geom::C_color);
00291   }
00292   if (get_uv_mode() != UV_none) {
00293     array_format->add_column
00294       (InternalName::get_texcoord(), 2, Geom::NT_float32,
00295        Geom::C_texcoord);
00296   }
00297 
00298   return GeomVertexFormat::register_format(array_format);
00299 }
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: RopeNode::do_recompute_bounds
00303 //       Access: Private
00304 //  Description: Does the actual internal recompute.
00305 ////////////////////////////////////////////////////////////////////
00306 PT(BoundingVolume) RopeNode::
00307 do_recompute_bounds(const NodePath &rel_to, int pipeline_stage, 
00308                     Thread *current_thread) const {
00309   // TODO: fix the bounds so that it properly reflects the indicated
00310   // pipeline stage.  At the moment, we cheat and get some of the
00311   // properties from the current pipeline stage, the lazy way.
00312 
00313   // First, get ourselves a fresh, empty bounding volume.
00314   PT(BoundingVolume) bound = new BoundingSphere;
00315   
00316   NurbsCurveEvaluator *curve = get_curve();
00317   if (curve != (NurbsCurveEvaluator *)NULL) {
00318     pvector<LPoint3f> verts;
00319     get_curve()->get_vertices(verts, rel_to);
00320 
00321     if (has_matrix()) {
00322       // And then apply the indicated matrix.
00323       const LMatrix4f &mat = get_matrix();
00324       pvector<LPoint3f>::iterator vi;
00325       for (vi = verts.begin(); vi != verts.end(); ++vi) {
00326         (*vi) = (*vi) * mat;
00327       }
00328     }
00329     
00330     GeometricBoundingVolume *gbv;
00331     DCAST_INTO_R(gbv, bound, bound);
00332     gbv->around(&verts[0], &verts[0] + verts.size());
00333   }
00334   return bound;
00335 }
00336 
00337 ////////////////////////////////////////////////////////////////////
00338 //     Function: RopeNode::render_thread
00339 //       Access: Private
00340 //  Description: Draws the rope in RM_thread mode.  This uses a
00341 //               GeomLinestrip to draw the rope in the simplest
00342 //               possible method, generally resulting in a
00343 //               one-pixel-wide curve.
00344 //
00345 //               In this mode, the thickness parameter represents a
00346 //               thickness in pixels, and is passed to the linestrip.
00347 //               However, you should be aware the DirectX does not
00348 //               support line thickness.  This mode does not support
00349 //               per-vertex thickness.
00350 ////////////////////////////////////////////////////////////////////
00351 void RopeNode::
00352 render_thread(CullTraverser *trav, CullTraverserData &data, 
00353               NurbsCurveResult *result) const {
00354   CurveSegments curve_segments;
00355   get_connected_segments(curve_segments, result);
00356 
00357   // Now we have stored one or more sequences of vertices down the
00358   // center strips.  Go back through and calculate the vertices on
00359   // either side.
00360   PT(GeomVertexData) vdata = new GeomVertexData
00361     ("rope", get_format(false), Geom::UH_stream);
00362   
00363   compute_thread_vertices(vdata, curve_segments);
00364   
00365   PT(GeomLinestrips) strip = new GeomLinestrips(Geom::UH_stream);
00366   CurveSegments::const_iterator si;
00367   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00368     const CurveSegment &segment = (*si);
00369     
00370     strip->add_next_vertices(segment.size());
00371     strip->close_primitive();
00372   }
00373   
00374   PT(Geom) geom = new Geom(vdata);
00375   geom->add_primitive(strip);
00376   
00377   CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, get_thickness());
00378   CPT(RenderState) state = data._state->add_attrib(thick);
00379   if (get_use_vertex_color()) {
00380     state = state->add_attrib(ColorAttrib::make_vertex());
00381   }
00382   
00383   CullableObject *object = 
00384     new CullableObject(geom, state,
00385                        data.get_net_transform(trav),
00386                        data.get_modelview_transform(trav),
00387                        trav->get_gsg());
00388   trav->get_cull_handler()->record_object(object, trav);
00389 }
00390 
00391 ////////////////////////////////////////////////////////////////////
00392 //     Function: RopeNode::render_tape
00393 //       Access: Private
00394 //  Description: Draws the rope in RM_tape mode.  This draws a
00395 //               series of triangle strips oriented to be
00396 //               perpendicular to the tube_up vector.
00397 //
00398 //               In this mode, thickness is in spatial units, and
00399 //               determines the width of the triangle strips.
00400 ////////////////////////////////////////////////////////////////////
00401 void RopeNode::
00402 render_tape(CullTraverser *trav, CullTraverserData &data, 
00403             NurbsCurveResult *result) const {
00404   CurveSegments curve_segments;
00405   get_connected_segments(curve_segments, result);
00406 
00407   // Now we have stored one or more sequences of vertices down the
00408   // center strips.  Go back through and calculate the vertices on
00409   // either side.
00410   PT(GeomVertexData) vdata = new GeomVertexData
00411     ("rope", get_format(false), Geom::UH_stream);
00412   
00413   compute_billboard_vertices(vdata, -get_tube_up(), 
00414                              curve_segments, result);
00415   
00416   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
00417   CurveSegments::const_iterator si;
00418   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00419     const CurveSegment &segment = (*si);
00420     
00421     strip->add_next_vertices(segment.size() * 2);
00422     strip->close_primitive();
00423   }
00424   
00425   PT(Geom) geom = new Geom(vdata);
00426   geom->add_primitive(strip);
00427 
00428   CPT(RenderState) state = data._state;
00429   if (get_use_vertex_color()) {
00430     state = state->add_attrib(ColorAttrib::make_vertex());
00431   }
00432   
00433   CullableObject *object = 
00434     new CullableObject(geom, state,
00435                        data.get_net_transform(trav),
00436                        data.get_modelview_transform(trav),
00437                        trav->get_gsg());
00438   trav->get_cull_handler()->record_object(object, trav);
00439 }
00440 
00441 ////////////////////////////////////////////////////////////////////
00442 //     Function: RopeNode::render_billboard
00443 //       Access: Private
00444 //  Description: Draws the rope in RM_billboard mode.  This draws a
00445 //               series of triangle strips oriented to be
00446 //               perpendicular to the camera plane.
00447 //
00448 //               In this mode, thickness is in spatial units, and
00449 //               determines the width of the triangle strips.
00450 ////////////////////////////////////////////////////////////////////
00451 void RopeNode::
00452 render_billboard(CullTraverser *trav, CullTraverserData &data, 
00453                  NurbsCurveResult *result) const {
00454   const TransformState *net_transform = data.get_net_transform(trav);
00455   const TransformState *camera_transform = trav->get_camera_transform();
00456 
00457   CPT(TransformState) rel_transform =
00458     net_transform->invert_compose(camera_transform);
00459   LVector3f camera_vec = LVector3f::forward() * rel_transform->get_mat();
00460 
00461   CurveSegments curve_segments;
00462   get_connected_segments(curve_segments, result);
00463 
00464   // Now we have stored one or more sequences of vertices down the
00465   // center strips.  Go back through and calculate the vertices on
00466   // either side.
00467   PT(GeomVertexData) vdata = new GeomVertexData
00468     ("rope", get_format(false), Geom::UH_stream);
00469   
00470   compute_billboard_vertices(vdata, camera_vec, 
00471                              curve_segments, result);
00472   
00473   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
00474   CurveSegments::const_iterator si;
00475   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00476     const CurveSegment &segment = (*si);
00477     
00478     strip->add_next_vertices(segment.size() * 2);
00479     strip->close_primitive();
00480   }
00481   
00482   PT(Geom) geom = new Geom(vdata);
00483   geom->add_primitive(strip);
00484 
00485   CPT(RenderState) state = data._state;
00486   if (get_use_vertex_color()) {
00487     state = state->add_attrib(ColorAttrib::make_vertex());
00488   }
00489   
00490   CullableObject *object = 
00491     new CullableObject(geom, state,
00492                        data.get_net_transform(trav),
00493                        data.get_modelview_transform(trav),
00494                        trav->get_gsg());
00495   trav->get_cull_handler()->record_object(object, trav);
00496 }
00497 
00498 ////////////////////////////////////////////////////////////////////
00499 //     Function: RopeNode::render_tube
00500 //       Access: Private
00501 //  Description: Draws the rope in RM_tube mode.  This draws a hollow
00502 //               tube centered around the string.
00503 //
00504 //               In this mode, thickness is in spatial units, and
00505 //               determines the diameter of the tube.
00506 ////////////////////////////////////////////////////////////////////
00507 void RopeNode::
00508 render_tube(CullTraverser *trav, CullTraverserData &data, 
00509             NurbsCurveResult *result) const {
00510   CurveSegments curve_segments;
00511   get_connected_segments(curve_segments, result);
00512 
00513   // Now, we build up a table of vertices, in a series of rings
00514   // around the circumference of the tube.
00515 
00516   int num_slices = get_num_slices();
00517   int num_verts_per_slice;
00518 
00519   PT(GeomVertexData) vdata = new GeomVertexData
00520     ("rope", get_format(true), Geom::UH_stream);
00521   
00522   compute_tube_vertices(vdata, num_verts_per_slice, 
00523                         curve_segments, result);
00524   
00525   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
00526   // Finally, go through build up the index array, to tie all the
00527   // triangle strips together.
00528   int vi = 0;
00529   CurveSegments::const_iterator si;
00530   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00531     const CurveSegment &segment = (*si);
00532     
00533     for (int s = 0; s < num_slices; ++s) {
00534       int s1 = (s + 1) % num_verts_per_slice;
00535       
00536       for (size_t j = 0; j < segment.size(); ++j) {
00537         strip->add_vertex((vi + j) * num_verts_per_slice + s);
00538         strip->add_vertex((vi + j) * num_verts_per_slice + s1);
00539       }
00540       
00541       strip->close_primitive();
00542     }
00543     vi += (int)segment.size();
00544   }
00545   
00546   PT(Geom) geom = new Geom(vdata);
00547   geom->add_primitive(strip);
00548 
00549   CPT(RenderState) state = data._state;
00550   if (get_use_vertex_color()) {
00551     state = state->add_attrib(ColorAttrib::make_vertex());
00552   }
00553   
00554   CullableObject *object = 
00555     new CullableObject(geom, state,
00556                        data.get_net_transform(trav),
00557                        data.get_modelview_transform(trav),
00558                        trav->get_gsg());
00559   trav->get_cull_handler()->record_object(object, trav);
00560 }
00561 
00562 ////////////////////////////////////////////////////////////////////
00563 //     Function: RopeNode::get_connected_segments
00564 //       Access: Private
00565 //  Description: Evaluates the string of vertices along the curve, and
00566 //               also breaks them up into connected segments.
00567 //
00568 //               Since the NurbsCurveEvaluator describes the curve as
00569 //               a sequence of possibly-connected piecewise continuous
00570 //               segments, this means joining together some adjacent
00571 //               segments from the NurbsCurveEvaluator into a single
00572 //               CurveSegment, if they happen to be connected (as most
00573 //               will be).
00574 ////////////////////////////////////////////////////////////////////
00575 void RopeNode::
00576 get_connected_segments(RopeNode::CurveSegments &curve_segments,
00577                        const NurbsCurveResult *result) const {
00578   int num_verts = get_num_subdiv() + 1;
00579   int num_segments = result->get_num_segments();
00580   bool use_vertex_color = get_use_vertex_color();
00581   bool use_vertex_thickness = get_use_vertex_thickness();
00582 
00583   CurveSegment *curve_segment = NULL;
00584   LPoint3f last_point;
00585 
00586   for (int segment = 0; segment < num_segments; ++segment) {
00587     LPoint3f point;
00588     result->eval_segment_point(segment, 0.0f, point);
00589 
00590     if (curve_segment == (CurveSegment *)NULL || 
00591         !point.almost_equal(last_point)) {
00592       // If the first point of this segment is different from the last
00593       // point of the previous segment, end the previous segment and
00594       // begin a new one.
00595       curve_segments.push_back(CurveSegment());
00596       curve_segment = &curve_segments.back();
00597 
00598       CurveVertex vtx;
00599       vtx._p = point;
00600       vtx._t = result->get_segment_t(segment, 0.0f);
00601       if (use_vertex_color) {
00602         result->eval_segment_extended_points(segment, 0.0f, 
00603                                              get_vertex_color_dimension(), 
00604                                              &vtx._c[0], 4);
00605       }
00606       if (use_vertex_thickness) {
00607         vtx._thickness = 
00608           result->eval_segment_extended_point(segment, 0.0f, 
00609                                               get_vertex_thickness_dimension());
00610       }
00611 
00612       curve_segment->push_back(vtx);
00613     }
00614 
00615     // Store all the remaining points in this segment.
00616     for (int i = 1; i < num_verts; ++i) {
00617       float t = (float)i / (float)(num_verts - 1);
00618 
00619       CurveVertex vtx;
00620       result->eval_segment_point(segment, t, vtx._p);
00621       vtx._t = result->get_segment_t(segment, t);
00622       if (use_vertex_color) {
00623         result->eval_segment_extended_points(segment, t, 
00624                                              get_vertex_color_dimension(),
00625                                              &vtx._c[0], 4);
00626       }
00627       if (use_vertex_thickness) {
00628         vtx._thickness = 
00629           result->eval_segment_extended_point(segment, t, 
00630                                               get_vertex_thickness_dimension());
00631       }
00632 
00633       curve_segment->push_back(vtx);
00634 
00635       last_point = vtx._p;
00636     }
00637   }
00638 }
00639 
00640 ////////////////////////////////////////////////////////////////////
00641 //     Function: RopeNode::compute_thread_vertices
00642 //       Access: Private
00643 //  Description: Calculates the vertices for a RM_thread render.  This
00644 //               just copies the vertices more-or-less directly into
00645 //               the array.
00646 ////////////////////////////////////////////////////////////////////
00647 void RopeNode::
00648 compute_thread_vertices(GeomVertexData *vdata,
00649                         const RopeNode::CurveSegments &curve_segments) const {
00650   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00651   GeomVertexWriter color(vdata, InternalName::get_color());
00652   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00653 
00654   UVMode uv_mode = get_uv_mode();
00655   float uv_scale = get_uv_scale();
00656   bool u_dominant = get_uv_direction();
00657   bool use_vertex_color = get_use_vertex_color();
00658 
00659   float dist = 0.0f;
00660   CurveSegments::const_iterator si;
00661   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00662     const CurveSegment &segment = (*si);
00663     for (size_t j = 0; j < segment.size(); ++j) {
00664       vertex.add_data3f(segment[j]._p);
00665 
00666       if (use_vertex_color) {
00667         color.add_data4f(segment[j]._c);
00668       }
00669 
00670       float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
00671 
00672       if (uv_mode != UV_none) {
00673         if (u_dominant) {
00674           texcoord.add_data2f(uv_t, 0.0f);
00675         } else {
00676           texcoord.add_data2f(0.0f, uv_t);
00677         }
00678       }
00679     }
00680   }
00681 }
00682 
00683 ////////////////////////////////////////////////////////////////////
00684 //     Function: RopeNode::compute_billboard_vertices
00685 //       Access: Private
00686 //  Description: Calculates the vertices for a RM_billboard render.  This
00687 //               puts a pair of vertices on either side of each
00688 //               computed point in curve_segments.
00689 ////////////////////////////////////////////////////////////////////
00690 void RopeNode::
00691 compute_billboard_vertices(GeomVertexData *vdata,
00692                            const LVector3f &camera_vec,
00693                            const RopeNode::CurveSegments &curve_segments,
00694                            NurbsCurveResult *result) const {
00695   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00696   GeomVertexWriter color(vdata, InternalName::get_color());
00697   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00698 
00699   float thickness = get_thickness();
00700   float overall_radius = thickness * 0.5f;
00701   float radius = overall_radius;
00702   UVMode uv_mode = get_uv_mode();
00703   float uv_scale = get_uv_scale();
00704   bool u_dominant = get_uv_direction();
00705   bool use_vertex_color = get_use_vertex_color();
00706   bool use_vertex_thickness = get_use_vertex_thickness();
00707 
00708   float dist = 0.0f;
00709   CurveSegments::const_iterator si;
00710   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00711     const CurveSegment &segment = (*si);
00712     for (size_t j = 0; j < segment.size(); ++j) {
00713       LVector3f tangent;
00714       compute_tangent(tangent, segment, j, result);
00715 
00716       LVector3f norm = cross(tangent, camera_vec);
00717       norm.normalize();
00718 
00719       if (use_vertex_thickness) {
00720         radius = overall_radius * segment[j]._thickness;
00721       }
00722 
00723       vertex.add_data3f(segment[j]._p + norm * radius);
00724       vertex.add_data3f(segment[j]._p - norm * radius);
00725 
00726       if (use_vertex_color) {
00727         color.add_data4f(segment[j]._c);
00728         color.add_data4f(segment[j]._c);
00729       }
00730 
00731       float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
00732 
00733       if (uv_mode != UV_none) {
00734         if (u_dominant) {
00735           texcoord.add_data2f(uv_t, 1.0f);
00736           texcoord.add_data2f(uv_t, 0.0f);
00737         } else {
00738           texcoord.add_data2f(1.0f, uv_t);
00739           texcoord.add_data2f(0.0f, uv_t);
00740         }
00741       }
00742     }
00743   }
00744 }
00745 
00746 ////////////////////////////////////////////////////////////////////
00747 //     Function: RopeNode::compute_tube_vertices
00748 //       Access: Private
00749 //  Description: Calculates the vertices for a RM_tube render.  This
00750 //               puts a ring of vertices around each computed point in
00751 //               curve_segments.
00752 ////////////////////////////////////////////////////////////////////
00753 void RopeNode::
00754 compute_tube_vertices(GeomVertexData *vdata,
00755                       int &num_verts_per_slice,
00756                       const RopeNode::CurveSegments &curve_segments,
00757                       NurbsCurveResult *result) const {
00758   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00759   GeomVertexWriter normal(vdata, InternalName::get_normal());
00760   GeomVertexWriter color(vdata, InternalName::get_color());
00761   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00762 
00763   int num_slices = get_num_slices();
00764   num_verts_per_slice = num_slices;
00765 
00766   float thickness = get_thickness();
00767   float overall_radius = thickness * 0.5f;
00768   float radius = overall_radius;
00769   UVMode uv_mode = get_uv_mode();
00770   float uv_scale = get_uv_scale();
00771   bool u_dominant = get_uv_direction();
00772   NormalMode normal_mode = get_normal_mode();
00773   bool use_vertex_color = get_use_vertex_color();
00774   bool use_vertex_thickness = get_use_vertex_thickness();
00775 
00776   // If we are generating UV's, we will need to duplicate the vertices
00777   // along the seam so that the UV's go through the whole range of
00778   // 0..1 instead of reflecting in the last polygon before the seam.
00779   if (uv_mode != UV_none) {
00780     ++num_verts_per_slice;
00781   }
00782 
00783   LVector3f up = get_tube_up();
00784 
00785   float dist = 0.0f;
00786   CurveSegments::const_iterator si;
00787   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00788     const CurveSegment &segment = (*si);
00789     for (size_t j = 0; j < segment.size(); ++j) {
00790       LVector3f tangent;
00791       compute_tangent(tangent, segment, j, result);
00792 
00793       LVector3f norm = cross(tangent, up);
00794       norm.normalize();
00795       up = cross(norm, tangent);
00796 
00797       LMatrix3f rotate = LMatrix3f::rotate_mat(360.0f / (float)num_slices,
00798                                                tangent);
00799 
00800       float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
00801 
00802       for (int s = 0; s < num_verts_per_slice; ++s) {
00803         if (use_vertex_thickness) {
00804           radius = overall_radius * segment[j]._thickness;
00805         }
00806 
00807         vertex.add_data3f(segment[j]._p + norm * radius);
00808 
00809         if (normal_mode == NM_vertex) {
00810           normal.add_data3f(norm);
00811         }
00812 
00813         if (use_vertex_color) {
00814           color.add_data4f(segment[j]._c);
00815         }
00816 
00817         norm = norm * rotate;
00818 
00819         if (uv_mode != UV_none) {
00820           float uv_s = (float)s / (float)num_slices;
00821           if (u_dominant) {
00822             texcoord.add_data2f(uv_t, uv_s);
00823           } else {
00824             texcoord.add_data2f(uv_s, uv_t);
00825           }
00826         }
00827       }
00828     }
00829   }
00830 }
00831 
00832 ////////////////////////////////////////////////////////////////////
00833 //     Function: RopeNode::compute_tangent
00834 //       Access: Private, Static
00835 //  Description: Computes the tangent to the curve at the indicated
00836 //               point in the segment.
00837 ////////////////////////////////////////////////////////////////////
00838 void RopeNode::
00839 compute_tangent(LVector3f &tangent, const RopeNode::CurveSegment &segment, 
00840                 size_t j, NurbsCurveResult *result) {
00841   // First, try to evaluate the tangent at the curve.  This gives
00842   // better results at the ends at the endpoints where the tangent
00843   // does not go to zero.
00844 
00845   /*
00846     Actually, on second thought this looks terrible.
00847 
00848   if (result->eval_tangent(segment[j]._t, tangent)) {
00849     if (!tangent.almost_equal(LVector3f::zero())) {
00850       return;
00851     }
00852   }
00853   */
00854 
00855   // If that failed (or produced a zero tangent), then derive the
00856   // tangent from the neighboring points instead.
00857   if (j == 0) {
00858     tangent = segment[j + 1]._p - segment[j]._p;
00859   } else if (j == segment.size() - 1) {
00860     tangent = segment[j]._p - segment[j - 1]._p;
00861   } else {
00862     tangent = segment[j + 1]._p - segment[j - 1]._p;
00863   }
00864 }
00865 
00866 ////////////////////////////////////////////////////////////////////
00867 //     Function: RopeNode::compute_uv_t
00868 //       Access: Private, Static
00869 //  Description: Computes the texture coordinate along the curve for
00870 //               the indicated point in the segment.
00871 ////////////////////////////////////////////////////////////////////
00872 float RopeNode::
00873 compute_uv_t(float &dist, const RopeNode::UVMode &uv_mode,
00874              float uv_scale, const RopeNode::CurveSegment &segment,
00875              size_t j) {
00876   switch (uv_mode) {
00877   case UV_none:
00878     return 0.0f;
00879     
00880   case UV_parametric:
00881     return segment[j]._t * uv_scale;
00882     
00883   case UV_distance:
00884     if (j != 0) {
00885       LVector3f vec = segment[j]._p - segment[j - 1]._p;
00886       dist += vec.length();
00887     }
00888     return dist * uv_scale;
00889     
00890   case UV_distance2:
00891     if (j != 0) {
00892       LVector3f vec = segment[j]._p - segment[j - 1]._p;
00893       dist += vec.length_squared();
00894     }
00895     return dist * uv_scale;
00896   }
00897 
00898   return 0.0f;
00899 }
00900   
00901 ////////////////////////////////////////////////////////////////////
00902 //     Function: RopeNode::register_with_read_factory
00903 //       Access: Public, Static
00904 //  Description: Tells the BamReader how to create objects of type
00905 //               RopeNode.
00906 ////////////////////////////////////////////////////////////////////
00907 void RopeNode::
00908 register_with_read_factory() {
00909   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
00910 }
00911 
00912 ////////////////////////////////////////////////////////////////////
00913 //     Function: RopeNode::write_datagram
00914 //       Access: Public, Virtual
00915 //  Description: Writes the contents of this object to the datagram
00916 //               for shipping out to a Bam file.
00917 ////////////////////////////////////////////////////////////////////
00918 void RopeNode::
00919 write_datagram(BamWriter *manager, Datagram &dg) {
00920   PandaNode::write_datagram(manager, dg);
00921   manager->write_cdata(dg, _cycler);
00922 }
00923 
00924 ////////////////////////////////////////////////////////////////////
00925 //     Function: RopeNode::make_from_bam
00926 //       Access: Protected, Static
00927 //  Description: This function is called by the BamReader's factory
00928 //               when a new object of type RopeNode is encountered
00929 //               in the Bam file.  It should create the RopeNode
00930 //               and extract its information from the file.
00931 ////////////////////////////////////////////////////////////////////
00932 TypedWritable *RopeNode::
00933 make_from_bam(const FactoryParams &params) {
00934   RopeNode *node = new RopeNode("");
00935   DatagramIterator scan;
00936   BamReader *manager;
00937 
00938   parse_params(params, scan, manager);
00939   node->fillin(scan, manager);
00940 
00941   return node;
00942 }
00943 
00944 ////////////////////////////////////////////////////////////////////
00945 //     Function: RopeNode::fillin
00946 //       Access: Protected
00947 //  Description: This internal function is called by make_from_bam to
00948 //               read in all of the relevant data from the BamFile for
00949 //               the new RopeNode.
00950 ////////////////////////////////////////////////////////////////////
00951 void RopeNode::
00952 fillin(DatagramIterator &scan, BamReader *manager) {
00953   PandaNode::fillin(scan, manager);
00954   manager->read_cdata(scan, _cycler);
00955 }
 All Classes Functions Variables Enumerations