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 "geomLines.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_stdfloat,
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_stdfloat,
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_stdfloat,
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     NurbsCurveEvaluator::Vert3Array verts;
00319     get_curve()->get_vertices(verts, rel_to);
00320 
00321     if (has_matrix()) {
00322       // And then apply the indicated matrix.
00323       const LMatrix4 &mat = get_matrix();
00324       NurbsCurveEvaluator::Vert3Array::iterator vi;
00325       for (vi = verts.begin(); vi != verts.end(); ++vi) {
00326         (*vi) = LPoint3(*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   int num_curve_verts = get_connected_segments(curve_segments, result);
00356 
00357   // Now we have stored one or more sequences of vertices down the
00358   // thread.  These map directly to primitive vertices.
00359   PT(GeomVertexData) vdata = new GeomVertexData
00360     ("rope", get_format(false), Geom::UH_stream);
00361   compute_thread_vertices(vdata, curve_segments, num_curve_verts);
00362 
00363   // We use GeomLines instead of GeomLinestrips, since that can more
00364   // easily be rendered directly.
00365   PT(GeomLines) lines = new GeomLines(Geom::UH_stream);
00366   lines->reserve_num_vertices((num_curve_verts - 1) * 2);
00367 
00368   for (int vi = 0; vi < num_curve_verts - 1; ++vi) {
00369     lines->add_vertex(vi);
00370     lines->add_vertex(vi + 1);
00371     lines->close_primitive();
00372   }
00373   
00374   PT(Geom) geom = new Geom(vdata);
00375   geom->add_primitive(lines);
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   int num_curve_verts = 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, num_curve_verts, result);
00415 
00416   // Since this will be a nonindexed primitive, no need to pre-reserve
00417   // the number of vertices.
00418   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
00419   CurveSegments::const_iterator si;
00420   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00421     const CurveSegment &segment = (*si);
00422     
00423     strip->add_next_vertices(segment.size() * 2);
00424     strip->close_primitive();
00425   }
00426   
00427   PT(Geom) geom = new Geom(vdata);
00428   geom->add_primitive(strip);
00429 
00430   CPT(RenderState) state = data._state;
00431   if (get_use_vertex_color()) {
00432     state = state->add_attrib(ColorAttrib::make_vertex());
00433   }
00434   
00435   CullableObject *object = 
00436     new CullableObject(geom, state,
00437                        data.get_net_transform(trav),
00438                        data.get_modelview_transform(trav),
00439                        trav->get_gsg());
00440   trav->get_cull_handler()->record_object(object, trav);
00441 }
00442 
00443 ////////////////////////////////////////////////////////////////////
00444 //     Function: RopeNode::render_billboard
00445 //       Access: Private
00446 //  Description: Draws the rope in RM_billboard mode.  This draws a
00447 //               series of triangle strips oriented to be
00448 //               perpendicular to the camera plane.
00449 //
00450 //               In this mode, thickness is in spatial units, and
00451 //               determines the width of the triangle strips.
00452 ////////////////////////////////////////////////////////////////////
00453 void RopeNode::
00454 render_billboard(CullTraverser *trav, CullTraverserData &data, 
00455                  NurbsCurveResult *result) const {
00456   const TransformState *net_transform = data.get_net_transform(trav);
00457   const TransformState *camera_transform = trav->get_camera_transform();
00458 
00459   CPT(TransformState) rel_transform =
00460     net_transform->invert_compose(camera_transform);
00461   LVector3 camera_vec = LVector3::forward() * rel_transform->get_mat();
00462 
00463   CurveSegments curve_segments;
00464   int num_curve_verts = get_connected_segments(curve_segments, result);
00465 
00466   // Now we have stored one or more sequences of vertices down the
00467   // center strips.  Go back through and calculate the vertices on
00468   // either side.
00469   PT(GeomVertexData) vdata = new GeomVertexData
00470     ("rope", get_format(false), Geom::UH_stream);
00471   
00472   compute_billboard_vertices(vdata, camera_vec, 
00473                              curve_segments, num_curve_verts, result);
00474   
00475   // Since this will be a nonindexed primitive, no need to pre-reserve
00476   // the number of vertices.
00477   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
00478   CurveSegments::const_iterator si;
00479   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00480     const CurveSegment &segment = (*si);
00481     
00482     strip->add_next_vertices(segment.size() * 2);
00483     strip->close_primitive();
00484   }
00485   
00486   PT(Geom) geom = new Geom(vdata);
00487   geom->add_primitive(strip);
00488 
00489   CPT(RenderState) state = data._state;
00490   if (get_use_vertex_color()) {
00491     state = state->add_attrib(ColorAttrib::make_vertex());
00492   }
00493   
00494   CullableObject *object = 
00495     new CullableObject(geom, state,
00496                        data.get_net_transform(trav),
00497                        data.get_modelview_transform(trav),
00498                        trav->get_gsg());
00499   trav->get_cull_handler()->record_object(object, trav);
00500 }
00501 
00502 ////////////////////////////////////////////////////////////////////
00503 //     Function: RopeNode::render_tube
00504 //       Access: Private
00505 //  Description: Draws the rope in RM_tube mode.  This draws a hollow
00506 //               tube centered around the string.
00507 //
00508 //               In this mode, thickness is in spatial units, and
00509 //               determines the diameter of the tube.
00510 ////////////////////////////////////////////////////////////////////
00511 void RopeNode::
00512 render_tube(CullTraverser *trav, CullTraverserData &data, 
00513             NurbsCurveResult *result) const {
00514   CurveSegments curve_segments;
00515   int num_curve_verts = get_connected_segments(curve_segments, result);
00516 
00517   // Now, we build up a table of vertices, in a series of rings
00518   // around the circumference of the tube.
00519 
00520   int num_slices = get_num_slices();
00521   int num_verts_per_slice;
00522 
00523   PT(GeomVertexData) vdata = new GeomVertexData
00524     ("rope", get_format(true), Geom::UH_stream);
00525   
00526   compute_tube_vertices(vdata, num_verts_per_slice, 
00527                         curve_segments, num_curve_verts, result);
00528   
00529   // Finally, go through and build up the index array, to tie all the
00530   // triangle strips together.  This is difficult to pre-calculate the
00531   // number of vertices we'll use, so we'll just let it dynamically
00532   // allocate.
00533   PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
00534   int vi = 0;
00535   CurveSegments::const_iterator si;
00536   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00537     const CurveSegment &segment = (*si);
00538     
00539     for (int s = 0; s < num_slices; ++s) {
00540       int s1 = (s + 1) % num_verts_per_slice;
00541       
00542       for (size_t j = 0; j < segment.size(); ++j) {
00543         strip->add_vertex((vi + j) * num_verts_per_slice + s);
00544         strip->add_vertex((vi + j) * num_verts_per_slice + s1);
00545       }
00546       
00547       strip->close_primitive();
00548     }
00549     vi += (int)segment.size();
00550   }
00551   
00552   PT(Geom) geom = new Geom(vdata);
00553   geom->add_primitive(strip);
00554 
00555   CPT(RenderState) state = data._state;
00556   if (get_use_vertex_color()) {
00557     state = state->add_attrib(ColorAttrib::make_vertex());
00558   }
00559   
00560   CullableObject *object = 
00561     new CullableObject(geom, state,
00562                        data.get_net_transform(trav),
00563                        data.get_modelview_transform(trav),
00564                        trav->get_gsg());
00565   trav->get_cull_handler()->record_object(object, trav);
00566 }
00567 
00568 ////////////////////////////////////////////////////////////////////
00569 //     Function: RopeNode::get_connected_segments
00570 //       Access: Private
00571 //  Description: Evaluates the string of vertices along the curve, and
00572 //               also breaks them up into connected segments.
00573 //
00574 //               Since the NurbsCurveEvaluator describes the curve as
00575 //               a sequence of possibly-connected piecewise continuous
00576 //               segments, this means joining together some adjacent
00577 //               segments from the NurbsCurveEvaluator into a single
00578 //               CurveSegment, if they happen to be connected (as most
00579 //               will be).
00580 //
00581 //               The return value is the total number of points across
00582 //               all segments.
00583 ////////////////////////////////////////////////////////////////////
00584 int RopeNode::
00585 get_connected_segments(RopeNode::CurveSegments &curve_segments,
00586                        const NurbsCurveResult *result) const {
00587   int num_curve_verts = 0;
00588 
00589   int num_verts = get_num_subdiv() + 1;
00590   int num_segments = result->get_num_segments();
00591   bool use_vertex_color = get_use_vertex_color();
00592   bool use_vertex_thickness = get_use_vertex_thickness();
00593 
00594   CurveSegment *curve_segment = NULL;
00595   LPoint3 last_point;
00596 
00597   for (int segment = 0; segment < num_segments; ++segment) {
00598     LPoint3 point;
00599     result->eval_segment_point(segment, 0.0f, point);
00600 
00601     if (curve_segment == (CurveSegment *)NULL || 
00602         !point.almost_equal(last_point)) {
00603       // If the first point of this segment is different from the last
00604       // point of the previous segment, end the previous segment and
00605       // begin a new one.
00606       curve_segments.push_back(CurveSegment());
00607       curve_segment = &curve_segments.back();
00608 
00609       CurveVertex vtx;
00610       vtx._p = point;
00611       vtx._t = result->get_segment_t(segment, 0.0f);
00612       if (use_vertex_color) {
00613         result->eval_segment_extended_points(segment, 0.0f, 
00614                                              get_vertex_color_dimension(), 
00615                                              &vtx._c[0], 4);
00616       }
00617       if (use_vertex_thickness) {
00618         vtx._thickness = 
00619           result->eval_segment_extended_point(segment, 0.0f, 
00620                                               get_vertex_thickness_dimension());
00621       }
00622 
00623       curve_segment->push_back(vtx);
00624       ++num_curve_verts;
00625     }
00626 
00627     // Store all the remaining points in this segment.
00628     for (int i = 1; i < num_verts; ++i) {
00629       PN_stdfloat t = (PN_stdfloat)i / (PN_stdfloat)(num_verts - 1);
00630 
00631       CurveVertex vtx;
00632       result->eval_segment_point(segment, t, vtx._p);
00633       vtx._t = result->get_segment_t(segment, t);
00634       if (use_vertex_color) {
00635         result->eval_segment_extended_points(segment, t, 
00636                                              get_vertex_color_dimension(),
00637                                              &vtx._c[0], 4);
00638       }
00639       if (use_vertex_thickness) {
00640         vtx._thickness = 
00641           result->eval_segment_extended_point(segment, t, 
00642                                               get_vertex_thickness_dimension());
00643       }
00644 
00645       curve_segment->push_back(vtx);
00646       ++num_curve_verts;
00647 
00648       last_point = vtx._p;
00649     }
00650   }
00651 
00652   return num_curve_verts;
00653 }
00654 
00655 ////////////////////////////////////////////////////////////////////
00656 //     Function: RopeNode::compute_thread_vertices
00657 //       Access: Private
00658 //  Description: Calculates the vertices for a RM_thread render.  This
00659 //               just copies the vertices more-or-less directly into
00660 //               the array.
00661 ////////////////////////////////////////////////////////////////////
00662 void RopeNode::
00663 compute_thread_vertices(GeomVertexData *vdata,
00664                         const RopeNode::CurveSegments &curve_segments,
00665                         int num_curve_verts) const {
00666   vdata->set_num_rows(num_curve_verts);
00667 
00668   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00669   GeomVertexWriter color(vdata, InternalName::get_color());
00670   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00671 
00672   UVMode uv_mode = get_uv_mode();
00673   PN_stdfloat uv_scale = get_uv_scale();
00674   bool u_dominant = get_uv_direction();
00675   bool use_vertex_color = get_use_vertex_color();
00676 
00677   PN_stdfloat dist = 0.0f;
00678   CurveSegments::const_iterator si;
00679   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00680     const CurveSegment &segment = (*si);
00681     for (size_t j = 0; j < segment.size(); ++j) {
00682       vertex.add_data3(segment[j]._p);
00683 
00684       if (use_vertex_color) {
00685         color.add_data4(segment[j]._c);
00686       }
00687 
00688       PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
00689 
00690       if (uv_mode != UV_none) {
00691         if (u_dominant) {
00692           texcoord.add_data2(uv_t, 0.0f);
00693         } else {
00694           texcoord.add_data2(0.0f, uv_t);
00695         }
00696       }
00697     }
00698   }
00699 
00700   nassertv(vdata->get_num_rows() == num_curve_verts);
00701 }
00702 
00703 ////////////////////////////////////////////////////////////////////
00704 //     Function: RopeNode::compute_billboard_vertices
00705 //       Access: Private
00706 //  Description: Calculates the vertices for a RM_billboard render.  This
00707 //               puts a pair of vertices on either side of each
00708 //               computed point in curve_segments.
00709 ////////////////////////////////////////////////////////////////////
00710 void RopeNode::
00711 compute_billboard_vertices(GeomVertexData *vdata,
00712                            const LVector3 &camera_vec,
00713                            const RopeNode::CurveSegments &curve_segments,
00714                            int num_curve_verts,
00715                            NurbsCurveResult *result) const {
00716   int expected_num_verts = num_curve_verts * 2;
00717   vdata->set_num_rows(expected_num_verts);
00718 
00719   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00720   GeomVertexWriter color(vdata, InternalName::get_color());
00721   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00722 
00723   PN_stdfloat thickness = get_thickness();
00724   PN_stdfloat overall_radius = thickness * 0.5f;
00725   PN_stdfloat radius = overall_radius;
00726   UVMode uv_mode = get_uv_mode();
00727   PN_stdfloat uv_scale = get_uv_scale();
00728   bool u_dominant = get_uv_direction();
00729   bool use_vertex_color = get_use_vertex_color();
00730   bool use_vertex_thickness = get_use_vertex_thickness();
00731 
00732   PN_stdfloat dist = 0.0f;
00733   CurveSegments::const_iterator si;
00734   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00735     const CurveSegment &segment = (*si);
00736     for (size_t j = 0; j < segment.size(); ++j) {
00737       LVector3 tangent;
00738       compute_tangent(tangent, segment, j, result);
00739 
00740       LVector3 norm = cross(tangent, camera_vec);
00741       norm.normalize();
00742 
00743       if (use_vertex_thickness) {
00744         radius = overall_radius * segment[j]._thickness;
00745       }
00746 
00747       vertex.add_data3(segment[j]._p + norm * radius);
00748       vertex.add_data3(segment[j]._p - norm * radius);
00749 
00750       if (use_vertex_color) {
00751         color.add_data4(segment[j]._c);
00752         color.add_data4(segment[j]._c);
00753       }
00754 
00755       PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
00756 
00757       if (uv_mode != UV_none) {
00758         if (u_dominant) {
00759           texcoord.add_data2(uv_t, 1.0f);
00760           texcoord.add_data2(uv_t, 0.0f);
00761         } else {
00762           texcoord.add_data2(1.0f, uv_t);
00763           texcoord.add_data2(0.0f, uv_t);
00764         }
00765       }
00766     }
00767   }
00768 
00769   nassertv(vdata->get_num_rows() == expected_num_verts);
00770 }
00771 
00772 ////////////////////////////////////////////////////////////////////
00773 //     Function: RopeNode::compute_tube_vertices
00774 //       Access: Private
00775 //  Description: Calculates the vertices for a RM_tube render.  This
00776 //               puts a ring of vertices around each computed point in
00777 //               curve_segments.
00778 ////////////////////////////////////////////////////////////////////
00779 void RopeNode::
00780 compute_tube_vertices(GeomVertexData *vdata,
00781                       int &num_verts_per_slice,
00782                       const RopeNode::CurveSegments &curve_segments,
00783                       int num_curve_verts,
00784                       NurbsCurveResult *result) const {
00785   int num_slices = get_num_slices();
00786   num_verts_per_slice = num_slices;
00787 
00788   PN_stdfloat thickness = get_thickness();
00789   PN_stdfloat overall_radius = thickness * 0.5f;
00790   PN_stdfloat radius = overall_radius;
00791   UVMode uv_mode = get_uv_mode();
00792   PN_stdfloat uv_scale = get_uv_scale();
00793   bool u_dominant = get_uv_direction();
00794   NormalMode normal_mode = get_normal_mode();
00795   bool use_vertex_color = get_use_vertex_color();
00796   bool use_vertex_thickness = get_use_vertex_thickness();
00797 
00798   // If we are generating UV's, we will need to duplicate the vertices
00799   // along the seam so that the UV's go through the whole range of
00800   // 0..1 instead of reflecting in the last polygon before the seam.
00801   if (uv_mode != UV_none) {
00802     ++num_verts_per_slice;
00803   }
00804 
00805   int expected_num_verts = num_curve_verts * num_verts_per_slice;
00806   vdata->set_num_rows(expected_num_verts);
00807 
00808   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00809   GeomVertexWriter normal(vdata, InternalName::get_normal());
00810   GeomVertexWriter color(vdata, InternalName::get_color());
00811   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00812 
00813   LVector3 up = get_tube_up();
00814 
00815   PN_stdfloat dist = 0.0f;
00816   CurveSegments::const_iterator si;
00817   for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
00818     const CurveSegment &segment = (*si);
00819     for (size_t j = 0; j < segment.size(); ++j) {
00820       LVector3 tangent;
00821       compute_tangent(tangent, segment, j, result);
00822 
00823       LVector3 norm = cross(tangent, up);
00824       norm.normalize();
00825       up = cross(norm, tangent);
00826 
00827       LMatrix3 rotate = LMatrix3::rotate_mat(360.0f / (PN_stdfloat)num_slices,
00828                                                tangent);
00829 
00830       PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
00831 
00832       for (int s = 0; s < num_verts_per_slice; ++s) {
00833         if (use_vertex_thickness) {
00834           radius = overall_radius * segment[j]._thickness;
00835         }
00836 
00837         vertex.add_data3(segment[j]._p + norm * radius);
00838 
00839         if (normal_mode == NM_vertex) {
00840           normal.add_data3(norm);
00841         }
00842 
00843         if (use_vertex_color) {
00844           color.add_data4(segment[j]._c);
00845         }
00846 
00847         norm = norm * rotate;
00848 
00849         if (uv_mode != UV_none) {
00850           PN_stdfloat uv_s = (PN_stdfloat)s / (PN_stdfloat)num_slices;
00851           if (u_dominant) {
00852             texcoord.add_data2(uv_t, uv_s);
00853           } else {
00854             texcoord.add_data2(uv_s, uv_t);
00855           }
00856         }
00857       }
00858     }
00859   }
00860 
00861   nassertv(vdata->get_num_rows() == expected_num_verts);
00862 }
00863 
00864 ////////////////////////////////////////////////////////////////////
00865 //     Function: RopeNode::compute_tangent
00866 //       Access: Private, Static
00867 //  Description: Computes the tangent to the curve at the indicated
00868 //               point in the segment.
00869 ////////////////////////////////////////////////////////////////////
00870 void RopeNode::
00871 compute_tangent(LVector3 &tangent, const RopeNode::CurveSegment &segment, 
00872                 size_t j, NurbsCurveResult *result) {
00873   // First, try to evaluate the tangent at the curve.  This gives
00874   // better results at the ends at the endpoints where the tangent
00875   // does not go to zero.
00876 
00877   /*
00878     Actually, on second thought this looks terrible.
00879 
00880   if (result->eval_tangent(segment[j]._t, tangent)) {
00881     if (!tangent.almost_equal(LVector3::zero())) {
00882       return;
00883     }
00884   }
00885   */
00886 
00887   // If that failed (or produced a zero tangent), then derive the
00888   // tangent from the neighboring points instead.
00889   if (j == 0) {
00890     tangent = segment[j + 1]._p - segment[j]._p;
00891   } else if (j == segment.size() - 1) {
00892     tangent = segment[j]._p - segment[j - 1]._p;
00893   } else {
00894     tangent = segment[j + 1]._p - segment[j - 1]._p;
00895   }
00896 }
00897 
00898 ////////////////////////////////////////////////////////////////////
00899 //     Function: RopeNode::compute_uv_t
00900 //       Access: Private, Static
00901 //  Description: Computes the texture coordinate along the curve for
00902 //               the indicated point in the segment.
00903 ////////////////////////////////////////////////////////////////////
00904 PN_stdfloat RopeNode::
00905 compute_uv_t(PN_stdfloat &dist, const RopeNode::UVMode &uv_mode,
00906              PN_stdfloat uv_scale, const RopeNode::CurveSegment &segment,
00907              size_t j) {
00908   switch (uv_mode) {
00909   case UV_none:
00910     return 0.0f;
00911     
00912   case UV_parametric:
00913     return segment[j]._t * uv_scale;
00914     
00915   case UV_distance:
00916     if (j != 0) {
00917       LVector3 vec = segment[j]._p - segment[j - 1]._p;
00918       dist += vec.length();
00919     }
00920     return dist * uv_scale;
00921     
00922   case UV_distance2:
00923     if (j != 0) {
00924       LVector3 vec = segment[j]._p - segment[j - 1]._p;
00925       dist += vec.length_squared();
00926     }
00927     return dist * uv_scale;
00928   }
00929 
00930   return 0.0f;
00931 }
00932   
00933 ////////////////////////////////////////////////////////////////////
00934 //     Function: RopeNode::register_with_read_factory
00935 //       Access: Public, Static
00936 //  Description: Tells the BamReader how to create objects of type
00937 //               RopeNode.
00938 ////////////////////////////////////////////////////////////////////
00939 void RopeNode::
00940 register_with_read_factory() {
00941   BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
00942 }
00943 
00944 ////////////////////////////////////////////////////////////////////
00945 //     Function: RopeNode::write_datagram
00946 //       Access: Public, Virtual
00947 //  Description: Writes the contents of this object to the datagram
00948 //               for shipping out to a Bam file.
00949 ////////////////////////////////////////////////////////////////////
00950 void RopeNode::
00951 write_datagram(BamWriter *manager, Datagram &dg) {
00952   PandaNode::write_datagram(manager, dg);
00953   manager->write_cdata(dg, _cycler);
00954 }
00955 
00956 ////////////////////////////////////////////////////////////////////
00957 //     Function: RopeNode::make_from_bam
00958 //       Access: Protected, Static
00959 //  Description: This function is called by the BamReader's factory
00960 //               when a new object of type RopeNode is encountered
00961 //               in the Bam file.  It should create the RopeNode
00962 //               and extract its information from the file.
00963 ////////////////////////////////////////////////////////////////////
00964 TypedWritable *RopeNode::
00965 make_from_bam(const FactoryParams &params) {
00966   RopeNode *node = new RopeNode("");
00967   DatagramIterator scan;
00968   BamReader *manager;
00969 
00970   parse_params(params, scan, manager);
00971   node->fillin(scan, manager);
00972 
00973   return node;
00974 }
00975 
00976 ////////////////////////////////////////////////////////////////////
00977 //     Function: RopeNode::fillin
00978 //       Access: Protected
00979 //  Description: This internal function is called by make_from_bam to
00980 //               read in all of the relevant data from the BamFile for
00981 //               the new RopeNode.
00982 ////////////////////////////////////////////////////////////////////
00983 void RopeNode::
00984 fillin(DatagramIterator &scan, BamReader *manager) {
00985   PandaNode::fillin(scan, manager);
00986   manager->read_cdata(scan, _cycler);
00987 }
 All Classes Functions Variables Enumerations