Panda3D
ropeNode.cxx
1 // Filename: ropeNode.cxx
2 // Created by: drose (04Dec02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "ropeNode.h"
16 #include "cullTraverser.h"
17 #include "cullTraverserData.h"
18 #include "cullableObject.h"
19 #include "cullHandler.h"
20 #include "renderState.h"
21 #include "renderModeAttrib.h"
22 #include "colorAttrib.h"
23 #include "bamWriter.h"
24 #include "bamReader.h"
25 #include "datagram.h"
26 #include "datagramIterator.h"
27 #include "pStatTimer.h"
28 #include "geom.h"
29 #include "geomLines.h"
30 #include "geomTristrips.h"
31 #include "geomVertexWriter.h"
32 #include "boundingSphere.h"
33 
34 TypeHandle RopeNode::_type_handle;
35 
36 PStatCollector RopeNode::_rope_node_pcollector("*:RopeNode");
37 
38 ////////////////////////////////////////////////////////////////////
39 // Function: RopeNode::CData::make_copy
40 // Access: Public, Virtual
41 // Description:
42 ////////////////////////////////////////////////////////////////////
43 CycleData *RopeNode::CData::
44 make_copy() const {
45  return new CData(*this);
46 }
47 
48 ////////////////////////////////////////////////////////////////////
49 // Function: RopeNode::CData::write_datagram
50 // Access: Public, Virtual
51 // Description: Writes the contents of this object to the datagram
52 // for shipping out to a Bam file.
53 ////////////////////////////////////////////////////////////////////
54 void RopeNode::CData::
55 write_datagram(BamWriter *writer, Datagram &dg) const {
56  // For now, we write a NULL pointer. Eventually we will write out
57  // the NurbsCurveEvaluator pointer.
58  writer->write_pointer(dg, (TypedWritable *)NULL);
59 }
60 
61 ////////////////////////////////////////////////////////////////////
62 // Function: RopeNode::CData::fillin
63 // Access: Public, Virtual
64 // Description: This internal function is called by make_from_bam to
65 // read in all of the relevant data from the BamFile for
66 // the new RopeNode.
67 ////////////////////////////////////////////////////////////////////
68 void RopeNode::CData::
69 fillin(DatagramIterator &scan, BamReader *reader) {
70  // For now, we skip over the NULL pointer that we wrote out.
71  reader->skip_pointer(scan);
72 }
73 
74 ////////////////////////////////////////////////////////////////////
75 // Function: RopeNode::Constructor
76 // Access: Public
77 // Description:
78 ////////////////////////////////////////////////////////////////////
79 RopeNode::
80 RopeNode(const string &name) :
81  PandaNode(name)
82 {
83  set_cull_callback();
84 }
85 
86 ////////////////////////////////////////////////////////////////////
87 // Function: RopeNode::Copy Constructor
88 // Access: Protected
89 // Description:
90 ////////////////////////////////////////////////////////////////////
91 RopeNode::
92 RopeNode(const RopeNode &copy) :
93  PandaNode(copy),
94  _cycler(copy._cycler)
95 {
96 }
97 
98 ////////////////////////////////////////////////////////////////////
99 // Function: RopeNode::make_copy
100 // Access: Public, Virtual
101 // Description: Returns a newly-allocated Node that is a shallow copy
102 // of this one. It will be a different Node pointer,
103 // but its internal data may or may not be shared with
104 // that of the original Node.
105 ////////////////////////////////////////////////////////////////////
107 make_copy() const {
108  return new RopeNode(*this);
109 }
110 
111 ////////////////////////////////////////////////////////////////////
112 // Function: RopeNode::safe_to_transform
113 // Access: Public, Virtual
114 // Description: Returns true if it is generally safe to transform
115 // this particular kind of Node by calling the xform()
116 // method, false otherwise. For instance, it's usually
117 // a bad idea to attempt to xform a RopeNode.
118 ////////////////////////////////////////////////////////////////////
119 bool RopeNode::
121  return false;
122 }
123 
124 ////////////////////////////////////////////////////////////////////
125 // Function: RopeNode::cull_callback
126 // Access: Public, Virtual
127 // Description: This function will be called during the cull
128 // traversal to perform any additional operations that
129 // should be performed at cull time. This may include
130 // additional manipulation of render state or additional
131 // visible/invisible decisions, or any other arbitrary
132 // operation.
133 //
134 // Note that this function will *not* be called unless
135 // set_cull_callback() is called in the constructor of
136 // the derived class. It is necessary to call
137 // set_cull_callback() to indicated that we require
138 // cull_callback() to be called.
139 //
140 // By the time this function is called, the node has
141 // already passed the bounding-volume test for the
142 // viewing frustum, and the node's transform and state
143 // have already been applied to the indicated
144 // CullTraverserData object.
145 //
146 // The return value is true if this node should be
147 // visible, or false if it should be culled.
148 ////////////////////////////////////////////////////////////////////
149 bool RopeNode::
151  // Statistics
152  PStatTimer timer(_rope_node_pcollector);
153 
154  // Create some geometry on-the-fly to render the rope.
155  if (get_num_subdiv() > 0) {
156  NurbsCurveEvaluator *curve = get_curve();
157  if (curve != (NurbsCurveEvaluator *)NULL) {
158  PT(NurbsCurveResult) result;
159  if (has_matrix()) {
160  result = curve->evaluate(data._node_path.get_node_path(), get_matrix());
161  } else {
162  result = curve->evaluate(data._node_path.get_node_path());
163  }
164 
165  if (result->get_num_segments() > 0) {
166  switch (get_render_mode()) {
167  case RM_thread:
168  render_thread(trav, data, result);
169  break;
170 
171  case RM_tape:
172  render_tape(trav, data, result);
173  break;
174 
175  case RM_billboard:
176  render_billboard(trav, data, result);
177  break;
178 
179  case RM_tube:
180  render_tube(trav, data, result);
181  break;
182  }
183  }
184  }
185  }
186 
187  return true;
188 }
189 
190 ////////////////////////////////////////////////////////////////////
191 // Function: RopeNode::is_renderable
192 // Access: Public, Virtual
193 // Description: Returns true if there is some value to visiting this
194 // particular node during the cull traversal for any
195 // camera, false otherwise. This will be used to
196 // optimize the result of get_net_draw_show_mask(), so
197 // that any subtrees that contain only nodes for which
198 // is_renderable() is false need not be visited.
199 ////////////////////////////////////////////////////////////////////
200 bool RopeNode::
201 is_renderable() const {
202  return true;
203 }
204 
205 ////////////////////////////////////////////////////////////////////
206 // Function: RopeNode::output
207 // Access: Public, Virtual
208 // Description:
209 ////////////////////////////////////////////////////////////////////
210 void RopeNode::
211 output(ostream &out) const {
212  PandaNode::output(out);
213  NurbsCurveEvaluator *curve = get_curve();
214  if (curve != (NurbsCurveEvaluator *)NULL) {
215  out << " " << *curve;
216  } else {
217  out << " (no curve)";
218  }
219 }
220 
221 ////////////////////////////////////////////////////////////////////
222 // Function: RopeNode::write
223 // Access: Public, Virtual
224 // Description:
225 ////////////////////////////////////////////////////////////////////
226 void RopeNode::
227 write(ostream &out, int indent_level) const {
228  PandaNode::write(out, indent_level);
229  indent(out, indent_level) << *get_curve() << "\n";
230 }
231 
232 ////////////////////////////////////////////////////////////////////
233 // Function: RopeNode::reset_bound
234 // Access: Published
235 // Description: Recomputes the bounding volume. This is normally
236 // called automatically, but it must occasionally be
237 // called explicitly when the curve has changed
238 // properties outside of this node's knowledge.
239 ////////////////////////////////////////////////////////////////////
240 void RopeNode::
241 reset_bound(const NodePath &rel_to) {
242  Thread *current_thread = Thread::get_current_thread();
243  int pipeline_stage = current_thread->get_pipeline_stage();
244  do_recompute_bounds(rel_to, pipeline_stage, current_thread);
245  mark_internal_bounds_stale(current_thread);
246 }
247 
248 ////////////////////////////////////////////////////////////////////
249 // Function: RopeNode::compute_internal_bounds
250 // Access: Protected, Virtual
251 // Description: Called when needed to recompute the node's
252 // _internal_bound object. Nodes that contain anything
253 // of substance should redefine this to do the right
254 // thing.
255 ////////////////////////////////////////////////////////////////////
256 void RopeNode::
257 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
258  int &internal_vertices,
259  int pipeline_stage,
260  Thread *current_thread) const {
261  PT(BoundingVolume) bounds =
262  do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage,
263  current_thread);
264 
265  internal_bounds = bounds;
266  internal_vertices = 0; // TODO--estimate this better.
267 }
268 
269 ////////////////////////////////////////////////////////////////////
270 // Function: RopeNode::get_format
271 // Access: Private
272 // Description: Returns the appropriate GeomVertexFormat for
273 // rendering, according to the user-specified
274 // requirements.
275 ////////////////////////////////////////////////////////////////////
276 CPT(GeomVertexFormat) RopeNode::
277 get_format(bool support_normals) const {
278  PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat
279  (InternalName::get_vertex(), 3, Geom::NT_stdfloat,
280  Geom::C_point);
281 
282  if (support_normals && get_normal_mode() == NM_vertex) {
283  array_format->add_column
284  (InternalName::get_normal(), 3, Geom::NT_stdfloat,
285  Geom::C_normal);
286  }
287  if (get_use_vertex_color()) {
288  array_format->add_column
289  (InternalName::get_color(), 1, Geom::NT_packed_dabc,
290  Geom::C_color);
291  }
292  if (get_uv_mode() != UV_none) {
293  array_format->add_column
294  (InternalName::get_texcoord(), 2, Geom::NT_stdfloat,
295  Geom::C_texcoord);
296  }
297 
298  return GeomVertexFormat::register_format(array_format);
299 }
300 
301 ////////////////////////////////////////////////////////////////////
302 // Function: RopeNode::do_recompute_bounds
303 // Access: Private
304 // Description: Does the actual internal recompute.
305 ////////////////////////////////////////////////////////////////////
306 PT(BoundingVolume) RopeNode::
307 do_recompute_bounds(const NodePath &rel_to, int pipeline_stage,
308  Thread *current_thread) const {
309  // TODO: fix the bounds so that it properly reflects the indicated
310  // pipeline stage. At the moment, we cheat and get some of the
311  // properties from the current pipeline stage, the lazy way.
312 
313  // First, get ourselves a fresh, empty bounding volume.
314  PT(BoundingVolume) bound = new BoundingSphere;
315 
316  NurbsCurveEvaluator *curve = get_curve();
317  if (curve != (NurbsCurveEvaluator *)NULL) {
319  get_curve()->get_vertices(verts, rel_to);
320 
321  if (has_matrix()) {
322  // And then apply the indicated matrix.
323  const LMatrix4 &mat = get_matrix();
324  NurbsCurveEvaluator::Vert3Array::iterator vi;
325  for (vi = verts.begin(); vi != verts.end(); ++vi) {
326  (*vi) = LPoint3(*vi) * mat;
327  }
328  }
329 
331  DCAST_INTO_R(gbv, bound, bound);
332  gbv->around(&verts[0], &verts[0] + verts.size());
333  }
334  return bound;
335 }
336 
337 ////////////////////////////////////////////////////////////////////
338 // Function: RopeNode::render_thread
339 // Access: Private
340 // Description: Draws the rope in RM_thread mode. This uses a
341 // GeomLinestrip to draw the rope in the simplest
342 // possible method, generally resulting in a
343 // one-pixel-wide curve.
344 //
345 // In this mode, the thickness parameter represents a
346 // thickness in pixels, and is passed to the linestrip.
347 // However, you should be aware the DirectX does not
348 // support line thickness. This mode does not support
349 // per-vertex thickness.
350 ////////////////////////////////////////////////////////////////////
351 void RopeNode::
352 render_thread(CullTraverser *trav, CullTraverserData &data,
353  NurbsCurveResult *result) const {
354  CurveSegments curve_segments;
355  int num_curve_verts = get_connected_segments(curve_segments, result);
356 
357  // Now we have stored one or more sequences of vertices down the
358  // thread. These map directly to primitive vertices.
359  PT(GeomVertexData) vdata = new GeomVertexData
360  ("rope", get_format(false), Geom::UH_stream);
361  compute_thread_vertices(vdata, curve_segments, num_curve_verts);
362 
363  // We use GeomLines instead of GeomLinestrips, since that can more
364  // easily be rendered directly.
365  PT(GeomLines) lines = new GeomLines(Geom::UH_stream);
366  lines->reserve_num_vertices((num_curve_verts - 1) * 2);
367 
368  for (int vi = 0; vi < num_curve_verts - 1; ++vi) {
369  lines->add_vertex(vi);
370  lines->add_vertex(vi + 1);
371  lines->close_primitive();
372  }
373 
374  PT(Geom) geom = new Geom(vdata);
375  geom->add_primitive(lines);
376 
377  CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, get_thickness());
378  CPT(RenderState) state = data._state->add_attrib(thick);
379  if (get_use_vertex_color()) {
380  state = state->add_attrib(ColorAttrib::make_vertex());
381  }
382 
383  CullableObject *object =
384  new CullableObject(geom, state,
385  data.get_internal_transform(trav));
386  trav->get_cull_handler()->record_object(object, trav);
387 }
388 
389 ////////////////////////////////////////////////////////////////////
390 // Function: RopeNode::render_tape
391 // Access: Private
392 // Description: Draws the rope in RM_tape mode. This draws a
393 // series of triangle strips oriented to be
394 // perpendicular to the tube_up vector.
395 //
396 // In this mode, thickness is in spatial units, and
397 // determines the width of the triangle strips.
398 ////////////////////////////////////////////////////////////////////
399 void RopeNode::
400 render_tape(CullTraverser *trav, CullTraverserData &data,
401  NurbsCurveResult *result) const {
402  CurveSegments curve_segments;
403  int num_curve_verts = get_connected_segments(curve_segments, result);
404 
405  // Now we have stored one or more sequences of vertices down the
406  // center strips. Go back through and calculate the vertices on
407  // either side.
408  PT(GeomVertexData) vdata = new GeomVertexData
409  ("rope", get_format(false), Geom::UH_stream);
410 
411  compute_billboard_vertices(vdata, -get_tube_up(),
412  curve_segments, num_curve_verts, result);
413 
414  // Since this will be a nonindexed primitive, no need to pre-reserve
415  // the number of vertices.
416  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
417  CurveSegments::const_iterator si;
418  for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
419  const CurveSegment &segment = (*si);
420 
421  strip->add_next_vertices(segment.size() * 2);
422  strip->close_primitive();
423  }
424 
425  PT(Geom) geom = new Geom(vdata);
426  geom->add_primitive(strip);
427 
428  CPT(RenderState) state = data._state;
429  if (get_use_vertex_color()) {
430  state = state->add_attrib(ColorAttrib::make_vertex());
431  }
432 
433  CullableObject *object =
434  new CullableObject(geom, state,
435  data.get_internal_transform(trav));
436  trav->get_cull_handler()->record_object(object, trav);
437 }
438 
439 ////////////////////////////////////////////////////////////////////
440 // Function: RopeNode::render_billboard
441 // Access: Private
442 // Description: Draws the rope in RM_billboard mode. This draws a
443 // series of triangle strips oriented to be
444 // perpendicular to the camera plane.
445 //
446 // In this mode, thickness is in spatial units, and
447 // determines the width of the triangle strips.
448 ////////////////////////////////////////////////////////////////////
449 void RopeNode::
450 render_billboard(CullTraverser *trav, CullTraverserData &data,
451  NurbsCurveResult *result) const {
452  const TransformState *net_transform = data.get_net_transform(trav);
453  const TransformState *camera_transform = trav->get_camera_transform();
454 
455  CPT(TransformState) rel_transform =
456  net_transform->invert_compose(camera_transform);
457  LVector3 camera_vec = LVector3::forward() * rel_transform->get_mat();
458 
459  CurveSegments curve_segments;
460  int num_curve_verts = get_connected_segments(curve_segments, result);
461 
462  // Now we have stored one or more sequences of vertices down the
463  // center strips. Go back through and calculate the vertices on
464  // either side.
465  PT(GeomVertexData) vdata = new GeomVertexData
466  ("rope", get_format(false), Geom::UH_stream);
467 
468  compute_billboard_vertices(vdata, camera_vec,
469  curve_segments, num_curve_verts, result);
470 
471  // Since this will be a nonindexed primitive, no need to pre-reserve
472  // the number of vertices.
473  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
474  CurveSegments::const_iterator si;
475  for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
476  const CurveSegment &segment = (*si);
477 
478  strip->add_next_vertices(segment.size() * 2);
479  strip->close_primitive();
480  }
481 
482  PT(Geom) geom = new Geom(vdata);
483  geom->add_primitive(strip);
484 
485  CPT(RenderState) state = data._state;
486  if (get_use_vertex_color()) {
487  state = state->add_attrib(ColorAttrib::make_vertex());
488  }
489 
490  CullableObject *object =
491  new CullableObject(geom, state,
492  data.get_internal_transform(trav));
493  trav->get_cull_handler()->record_object(object, trav);
494 }
495 
496 ////////////////////////////////////////////////////////////////////
497 // Function: RopeNode::render_tube
498 // Access: Private
499 // Description: Draws the rope in RM_tube mode. This draws a hollow
500 // tube centered around the string.
501 //
502 // In this mode, thickness is in spatial units, and
503 // determines the diameter of the tube.
504 ////////////////////////////////////////////////////////////////////
505 void RopeNode::
506 render_tube(CullTraverser *trav, CullTraverserData &data,
507  NurbsCurveResult *result) const {
508  CurveSegments curve_segments;
509  int num_curve_verts = get_connected_segments(curve_segments, result);
510 
511  // Now, we build up a table of vertices, in a series of rings
512  // around the circumference of the tube.
513 
514  int num_slices = get_num_slices();
515  int num_verts_per_slice;
516 
517  PT(GeomVertexData) vdata = new GeomVertexData
518  ("rope", get_format(true), Geom::UH_stream);
519 
520  compute_tube_vertices(vdata, num_verts_per_slice,
521  curve_segments, num_curve_verts, result);
522 
523  // Finally, go through and build up the index array, to tie all the
524  // triangle strips together. This is difficult to pre-calculate the
525  // number of vertices we'll use, so we'll just let it dynamically
526  // allocate.
527  PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream);
528  int vi = 0;
529  CurveSegments::const_iterator si;
530  for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
531  const CurveSegment &segment = (*si);
532 
533  for (int s = 0; s < num_slices; ++s) {
534  int s1 = (s + 1) % num_verts_per_slice;
535 
536  for (size_t j = 0; j < segment.size(); ++j) {
537  strip->add_vertex((vi + j) * num_verts_per_slice + s);
538  strip->add_vertex((vi + j) * num_verts_per_slice + s1);
539  }
540 
541  strip->close_primitive();
542  }
543  vi += (int)segment.size();
544  }
545 
546  PT(Geom) geom = new Geom(vdata);
547  geom->add_primitive(strip);
548 
549  CPT(RenderState) state = data._state;
550  if (get_use_vertex_color()) {
551  state = state->add_attrib(ColorAttrib::make_vertex());
552  }
553 
554  CullableObject *object =
555  new CullableObject(geom, state,
556  data.get_internal_transform(trav));
557  trav->get_cull_handler()->record_object(object, trav);
558 }
559 
560 ////////////////////////////////////////////////////////////////////
561 // Function: RopeNode::get_connected_segments
562 // Access: Private
563 // Description: Evaluates the string of vertices along the curve, and
564 // also breaks them up into connected segments.
565 //
566 // Since the NurbsCurveEvaluator describes the curve as
567 // a sequence of possibly-connected piecewise continuous
568 // segments, this means joining together some adjacent
569 // segments from the NurbsCurveEvaluator into a single
570 // CurveSegment, if they happen to be connected (as most
571 // will be).
572 //
573 // The return value is the total number of points across
574 // all segments.
575 ////////////////////////////////////////////////////////////////////
576 int RopeNode::
577 get_connected_segments(RopeNode::CurveSegments &curve_segments,
578  const NurbsCurveResult *result) const {
579  int num_curve_verts = 0;
580 
581  int num_verts = get_num_subdiv() + 1;
582  int num_segments = result->get_num_segments();
583  bool use_vertex_color = get_use_vertex_color();
584  bool use_vertex_thickness = get_use_vertex_thickness();
585 
586  CurveSegment *curve_segment = NULL;
587  LPoint3 last_point;
588 
589  for (int segment = 0; segment < num_segments; ++segment) {
590  LPoint3 point;
591  result->eval_segment_point(segment, 0.0f, point);
592 
593  if (curve_segment == (CurveSegment *)NULL ||
594  !point.almost_equal(last_point)) {
595  // If the first point of this segment is different from the last
596  // point of the previous segment, end the previous segment and
597  // begin a new one.
598  curve_segments.push_back(CurveSegment());
599  curve_segment = &curve_segments.back();
600 
601  CurveVertex vtx;
602  vtx._p = point;
603  vtx._t = result->get_segment_t(segment, 0.0f);
604  if (use_vertex_color) {
605  result->eval_segment_extended_points(segment, 0.0f,
607  &vtx._c[0], 4);
608  }
609  if (use_vertex_thickness) {
610  vtx._thickness =
611  result->eval_segment_extended_point(segment, 0.0f,
613  }
614 
615  curve_segment->push_back(vtx);
616  ++num_curve_verts;
617  }
618 
619  // Store all the remaining points in this segment.
620  for (int i = 1; i < num_verts; ++i) {
621  PN_stdfloat t = (PN_stdfloat)i / (PN_stdfloat)(num_verts - 1);
622 
623  CurveVertex vtx;
624  result->eval_segment_point(segment, t, vtx._p);
625  vtx._t = result->get_segment_t(segment, t);
626  if (use_vertex_color) {
627  result->eval_segment_extended_points(segment, t,
629  &vtx._c[0], 4);
630  }
631  if (use_vertex_thickness) {
632  vtx._thickness =
633  result->eval_segment_extended_point(segment, t,
635  }
636 
637  curve_segment->push_back(vtx);
638  ++num_curve_verts;
639 
640  last_point = vtx._p;
641  }
642  }
643 
644  return num_curve_verts;
645 }
646 
647 ////////////////////////////////////////////////////////////////////
648 // Function: RopeNode::compute_thread_vertices
649 // Access: Private
650 // Description: Calculates the vertices for a RM_thread render. This
651 // just copies the vertices more-or-less directly into
652 // the array.
653 ////////////////////////////////////////////////////////////////////
654 void RopeNode::
655 compute_thread_vertices(GeomVertexData *vdata,
656  const RopeNode::CurveSegments &curve_segments,
657  int num_curve_verts) const {
658  vdata->set_num_rows(num_curve_verts);
659 
660  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
661  GeomVertexWriter color(vdata, InternalName::get_color());
662  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
663 
664  UVMode uv_mode = get_uv_mode();
665  PN_stdfloat uv_scale = get_uv_scale();
666  bool u_dominant = get_uv_direction();
667  bool use_vertex_color = get_use_vertex_color();
668 
669  PN_stdfloat dist = 0.0f;
670  CurveSegments::const_iterator si;
671  for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
672  const CurveSegment &segment = (*si);
673  for (size_t j = 0; j < segment.size(); ++j) {
674  vertex.add_data3(segment[j]._p);
675 
676  if (use_vertex_color) {
677  color.add_data4(segment[j]._c);
678  }
679 
680  PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
681 
682  if (uv_mode != UV_none) {
683  if (u_dominant) {
684  texcoord.add_data2(uv_t, 0.0f);
685  } else {
686  texcoord.add_data2(0.0f, uv_t);
687  }
688  }
689  }
690  }
691 
692  nassertv(vdata->get_num_rows() == num_curve_verts);
693 }
694 
695 ////////////////////////////////////////////////////////////////////
696 // Function: RopeNode::compute_billboard_vertices
697 // Access: Private
698 // Description: Calculates the vertices for a RM_billboard render. This
699 // puts a pair of vertices on either side of each
700 // computed point in curve_segments.
701 ////////////////////////////////////////////////////////////////////
702 void RopeNode::
703 compute_billboard_vertices(GeomVertexData *vdata,
704  const LVector3 &camera_vec,
705  const RopeNode::CurveSegments &curve_segments,
706  int num_curve_verts,
707  NurbsCurveResult *result) const {
708  int expected_num_verts = num_curve_verts * 2;
709  vdata->set_num_rows(expected_num_verts);
710 
711  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
712  GeomVertexWriter color(vdata, InternalName::get_color());
713  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
714 
715  PN_stdfloat thickness = get_thickness();
716  PN_stdfloat overall_radius = thickness * 0.5f;
717  PN_stdfloat radius = overall_radius;
718  UVMode uv_mode = get_uv_mode();
719  PN_stdfloat uv_scale = get_uv_scale();
720  bool u_dominant = get_uv_direction();
721  bool use_vertex_color = get_use_vertex_color();
722  bool use_vertex_thickness = get_use_vertex_thickness();
723 
724  PN_stdfloat dist = 0.0f;
725  CurveSegments::const_iterator si;
726  for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
727  const CurveSegment &segment = (*si);
728  for (size_t j = 0; j < segment.size(); ++j) {
729  LVector3 tangent;
730  compute_tangent(tangent, segment, j, result);
731 
732  LVector3 norm = cross(tangent, camera_vec);
733  norm.normalize();
734 
735  if (use_vertex_thickness) {
736  radius = overall_radius * segment[j]._thickness;
737  }
738 
739  vertex.add_data3(segment[j]._p + norm * radius);
740  vertex.add_data3(segment[j]._p - norm * radius);
741 
742  if (use_vertex_color) {
743  color.add_data4(segment[j]._c);
744  color.add_data4(segment[j]._c);
745  }
746 
747  PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
748 
749  if (uv_mode != UV_none) {
750  if (u_dominant) {
751  texcoord.add_data2(uv_t, 1.0f);
752  texcoord.add_data2(uv_t, 0.0f);
753  } else {
754  texcoord.add_data2(1.0f, uv_t);
755  texcoord.add_data2(0.0f, uv_t);
756  }
757  }
758  }
759  }
760 
761  nassertv(vdata->get_num_rows() == expected_num_verts);
762 }
763 
764 ////////////////////////////////////////////////////////////////////
765 // Function: RopeNode::compute_tube_vertices
766 // Access: Private
767 // Description: Calculates the vertices for a RM_tube render. This
768 // puts a ring of vertices around each computed point in
769 // curve_segments.
770 ////////////////////////////////////////////////////////////////////
771 void RopeNode::
772 compute_tube_vertices(GeomVertexData *vdata,
773  int &num_verts_per_slice,
774  const RopeNode::CurveSegments &curve_segments,
775  int num_curve_verts,
776  NurbsCurveResult *result) const {
777  int num_slices = get_num_slices();
778  num_verts_per_slice = num_slices;
779 
780  PN_stdfloat thickness = get_thickness();
781  PN_stdfloat overall_radius = thickness * 0.5f;
782  PN_stdfloat radius = overall_radius;
783  UVMode uv_mode = get_uv_mode();
784  PN_stdfloat uv_scale = get_uv_scale();
785  bool u_dominant = get_uv_direction();
786  NormalMode normal_mode = get_normal_mode();
787  bool use_vertex_color = get_use_vertex_color();
788  bool use_vertex_thickness = get_use_vertex_thickness();
789 
790  // If we are generating UV's, we will need to duplicate the vertices
791  // along the seam so that the UV's go through the whole range of
792  // 0..1 instead of reflecting in the last polygon before the seam.
793  if (uv_mode != UV_none) {
794  ++num_verts_per_slice;
795  }
796 
797  int expected_num_verts = num_curve_verts * num_verts_per_slice;
798  vdata->set_num_rows(expected_num_verts);
799 
800  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
801  GeomVertexWriter normal(vdata, InternalName::get_normal());
802  GeomVertexWriter color(vdata, InternalName::get_color());
803  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
804 
805  LVector3 up = get_tube_up();
806 
807  PN_stdfloat dist = 0.0f;
808  CurveSegments::const_iterator si;
809  for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
810  const CurveSegment &segment = (*si);
811  for (size_t j = 0; j < segment.size(); ++j) {
812  LVector3 tangent;
813  compute_tangent(tangent, segment, j, result);
814 
815  LVector3 norm = cross(tangent, up);
816 
817  // In case the tangent is linear dependent on the up vector, we might get invalid
818  // results, so check that
819  if (IS_NEARLY_ZERO(norm.length_squared())) {
820 
821  if (IS_NEARLY_ZERO(tangent.get_y()) && IS_NEARLY_ZERO(tangent.get_z())) {
822  // Vector is linear dependent on (1, 0, 0), use (0, 1, 0) as base
823  norm = cross(tangent, LVector3(0, 1, 0));
824  } else {
825  norm = cross(tangent, LVector3(1, 0, 0));
826  }
827  }
828 
829  norm.normalize();
830  up = cross(norm, tangent);
831 
832  LMatrix3 rotate = LMatrix3::rotate_mat(360.0f / (PN_stdfloat)num_slices,
833  tangent);
834 
835  PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
836 
837  for (int s = 0; s < num_verts_per_slice; ++s) {
838  if (use_vertex_thickness) {
839  radius = overall_radius * segment[j]._thickness;
840  }
841 
842  vertex.add_data3(segment[j]._p + norm * radius);
843 
844  if (normal_mode == NM_vertex) {
845  normal.add_data3(norm);
846  }
847 
848  if (use_vertex_color) {
849  color.add_data4(segment[j]._c);
850  }
851 
852  norm = norm * rotate;
853 
854  if (uv_mode != UV_none) {
855  PN_stdfloat uv_s = (PN_stdfloat)s / (PN_stdfloat)num_slices;
856  if (u_dominant) {
857  texcoord.add_data2(uv_t, uv_s);
858  } else {
859  texcoord.add_data2(uv_s, uv_t);
860  }
861  }
862  }
863  }
864  }
865 
866  nassertv(vdata->get_num_rows() == expected_num_verts);
867 }
868 
869 ////////////////////////////////////////////////////////////////////
870 // Function: RopeNode::compute_tangent
871 // Access: Private, Static
872 // Description: Computes the tangent to the curve at the indicated
873 // point in the segment.
874 ////////////////////////////////////////////////////////////////////
875 void RopeNode::
876 compute_tangent(LVector3 &tangent, const RopeNode::CurveSegment &segment,
877  size_t j, NurbsCurveResult *result) {
878  // First, try to evaluate the tangent at the curve. This gives
879  // better results at the ends at the endpoints where the tangent
880  // does not go to zero.
881 
882  /*
883  Actually, on second thought this looks terrible.
884 
885  if (result->eval_tangent(segment[j]._t, tangent)) {
886  if (!tangent.almost_equal(LVector3::zero())) {
887  return;
888  }
889  }
890  */
891 
892  // If that failed (or produced a zero tangent), then derive the
893  // tangent from the neighboring points instead.
894  if (j == 0) {
895  tangent = segment[j + 1]._p - segment[j]._p;
896  } else if (j == segment.size() - 1) {
897  tangent = segment[j]._p - segment[j - 1]._p;
898  } else {
899  tangent = segment[j + 1]._p - segment[j - 1]._p;
900  }
901 
902  // Avoid empty tangents, these lead to crashes. Instead, use an arbitrary
903  // tangent.
904  if (IS_NEARLY_ZERO(tangent.length_squared())) {
905  tangent.set(0, 0, 1);
906  }
907 
908 }
909 
910 ////////////////////////////////////////////////////////////////////
911 // Function: RopeNode::compute_uv_t
912 // Access: Private, Static
913 // Description: Computes the texture coordinate along the curve for
914 // the indicated point in the segment.
915 ////////////////////////////////////////////////////////////////////
916 PN_stdfloat RopeNode::
917 compute_uv_t(PN_stdfloat &dist, const RopeNode::UVMode &uv_mode,
918  PN_stdfloat uv_scale, const RopeNode::CurveSegment &segment,
919  size_t j) {
920  switch (uv_mode) {
921  case UV_none:
922  return 0.0f;
923 
924  case UV_parametric:
925  return segment[j]._t * uv_scale;
926 
927  case UV_distance:
928  if (j != 0) {
929  LVector3 vec = segment[j]._p - segment[j - 1]._p;
930  dist += vec.length();
931  }
932  return dist * uv_scale;
933 
934  case UV_distance2:
935  if (j != 0) {
936  LVector3 vec = segment[j]._p - segment[j - 1]._p;
937  dist += vec.length_squared();
938  }
939  return dist * uv_scale;
940  }
941 
942  return 0.0f;
943 }
944 
945 ////////////////////////////////////////////////////////////////////
946 // Function: RopeNode::register_with_read_factory
947 // Access: Public, Static
948 // Description: Tells the BamReader how to create objects of type
949 // RopeNode.
950 ////////////////////////////////////////////////////////////////////
951 void RopeNode::
953  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
954 }
955 
956 ////////////////////////////////////////////////////////////////////
957 // Function: RopeNode::write_datagram
958 // Access: Public, Virtual
959 // Description: Writes the contents of this object to the datagram
960 // for shipping out to a Bam file.
961 ////////////////////////////////////////////////////////////////////
962 void RopeNode::
964  PandaNode::write_datagram(manager, dg);
965  manager->write_cdata(dg, _cycler);
966 }
967 
968 ////////////////////////////////////////////////////////////////////
969 // Function: RopeNode::make_from_bam
970 // Access: Protected, Static
971 // Description: This function is called by the BamReader's factory
972 // when a new object of type RopeNode is encountered
973 // in the Bam file. It should create the RopeNode
974 // and extract its information from the file.
975 ////////////////////////////////////////////////////////////////////
976 TypedWritable *RopeNode::
977 make_from_bam(const FactoryParams &params) {
978  RopeNode *node = new RopeNode("");
979  DatagramIterator scan;
980  BamReader *manager;
981 
982  parse_params(params, scan, manager);
983  node->fillin(scan, manager);
984 
985  return node;
986 }
987 
988 ////////////////////////////////////////////////////////////////////
989 // Function: RopeNode::fillin
990 // Access: Protected
991 // Description: This internal function is called by make_from_bam to
992 // read in all of the relevant data from the BamFile for
993 // the new RopeNode.
994 ////////////////////////////////////////////////////////////////////
995 void RopeNode::
996 fillin(DatagramIterator &scan, BamReader *manager) {
997  PandaNode::fillin(scan, manager);
998  manager->read_cdata(scan, _cycler);
999 }
float length_squared() const
Returns the square of the vector&#39;s length, cheap and easy.
Definition: lvecBase3.h:749
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
void eval_segment_extended_points(int segment, PN_stdfloat t, int d, PN_stdfloat result[], int num_values) const
Simultaneously performs eval_extended_point on a contiguous sequence of dimensions.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
CullHandler * get_cull_handler() const
Returns the object that will receive the culled Geoms.
const TransformState * get_camera_transform() const
Returns the position of the camera relative to the starting node.
Definition: cullTraverser.I:76
UVMode get_uv_mode() const
Returns the algorithm to use to generate UV&#39;s for the rope.
Definition: ropeNode.I:130
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:60
PN_stdfloat eval_segment_extended_point(int segment, PN_stdfloat t, int d) const
Evaluates the curve in n-dimensional space according to the extended vertices associated with the cur...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: pandaNode.cxx:4164
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
int get_pipeline_stage() const
Returns the Pipeline stage number associated with this thread.
Definition: thread.I:84
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:753
int get_num_slices() const
Returns the number of radial subdivisions to make if RenderMode is RM_tube.
Definition: ropeNode.I:343
bool get_use_vertex_color() const
Returns the "use vertex color" flag.
Definition: ropeNode.I:269
This defines a bounding sphere, consisting of a center and a radius.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
This class is an abstraction for evaluating NURBS curves.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:398
void add_data2(PN_stdfloat x, PN_stdfloat y)
Sets the write row to a particular 2-component value, and advances the write row. ...
void skip_pointer(DatagramIterator &scan)
Reads and discards a pointer value from the Bam file.
Definition: bamReader.cxx:715
bool has_matrix() const
Returns true if the node has a matrix set, false otherwise.
Definition: ropeNode.I:456
This collects together the pieces of data that are accumulated for each node while walking the scene ...
const LMatrix4 & get_matrix() const
Returns the optional matrix which is used to transform each control vertex after it has been transfor...
Definition: ropeNode.I:470
This class draws a visible representation of the NURBS curve stored in its NurbsCurveEvaluator.
Definition: ropeNode.h:38
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
Defines a series of triangle strips.
Definition: geomTristrips.h:25
int get_num_subdiv() const
Returns the number of subdivisions per cubic segment to draw.
Definition: ropeNode.I:310
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
NormalMode get_normal_mode() const
Returns the kind of normals to generate for the rope.
Definition: ropeNode.I:208
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
bool get_uv_direction() const
Returns true if the rope runs down the U coordinate of the texture, or false if it runs down the V co...
Definition: ropeNode.I:155
void add_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w)
Sets the write row to a particular 4-component value, and advances the write row. ...
bool almost_equal(const LVecBase3f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase3.h:1280
PN_stdfloat get_uv_scale() const
Returns the scaling factor to apply to generated UV&#39;s for the rope.
Definition: ropeNode.I:181
void eval_segment_point(int segment, PN_stdfloat t, LVecBase3 &point) const
Evaluates the point on the curve corresponding to the indicated value in parametric time within the i...
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
virtual bool safe_to_transform() const
Returns true if it is generally safe to transform this particular kind of Node by calling the xform()...
Definition: ropeNode.cxx:120
static LVector3f forward(CoordinateSystem cs=CS_default)
Returns the forward vector for the given coordinate system.
Definition: lvector3.h:579
PN_stdfloat get_segment_t(int segment, PN_stdfloat t) const
Accepts a t value in the range [0, 1], and assumed to be relative to the indicated segment (as in eva...
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
A lightweight class that represents a single element that may be timed and/or counted via stats...
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
NodePath get_node_path() const
Constructs and returns an actual NodePath that represents the same path we have just traversed...
bool get_use_vertex_thickness() const
Returns the "use vertex thickness" flag.
Definition: ropeNode.I:375
The smallest atom of cull.
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
virtual void record_object(CullableObject *object, const CullTraverser *traverser)
This callback function is intended to be overridden by a derived class.
Definition: cullHandler.cxx:52
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
bool set_num_rows(int n)
Sets the length of the array to n rows in all of the various arrays (presumably by adding rows)...
int get_num_rows() const
Returns the number of rows stored within all the arrays.
int get_num_segments() const
Returns the number of piecewise continuous segments within the curve.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
const LVector3 & get_tube_up() const
Returns the normal vector used to control the "top" of the curve, when RenderMode is RM_tube...
Definition: ropeNode.I:239
Defines a series of disconnected line segments.
Definition: geomLines.h:25
RenderMode get_render_mode() const
Returns the method used to render the rope.
Definition: ropeNode.I:106
float length() const
Returns the length of the vector, by the Pythagorean theorem.
Definition: lvecBase3.h:766
static void register_with_read_factory()
Tells the BamReader how to create objects of type RopeNode.
Definition: ropeNode.cxx:952
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
void reset_bound(const NodePath &rel_to)
Recomputes the bounding volume.
Definition: ropeNode.cxx:241
A thread; that is, a lightweight process.
Definition: thread.h:51
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition: ropeNode.cxx:107
static int get_vertex_thickness_dimension()
Returns the numeric extended dimension in which the thickness component should be found...
Definition: ropeNode.I:388
static LMatrix3f rotate_mat(float angle)
Returns a matrix that rotates by the given angle in degrees counterclockwise.
Definition: lmatrix.h:4081
PN_stdfloat get_thickness() const
Returns the thickness of the rope.
Definition: ropeNode.I:416
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
Definition: ropeNode.cxx:150
A class to retrieve the individual data elements previously stored in a Datagram. ...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: ropeNode.cxx:963
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
This is a 3-by-3 transform matrix.
Definition: lmatrix.h:110
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
NurbsCurveEvaluator * get_curve() const
Returns the curve represented by the RopeNode.
Definition: ropeNode.I:81
bool around(const GeometricBoundingVolume **first, const GeometricBoundingVolume **last)
Resets the volume to enclose only the volumes indicated.
The result of a NurbsCurveEvaluator.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
static int get_vertex_color_dimension()
Returns the numeric extended dimension in which the color components should be found.
Definition: ropeNode.I:285
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:279
virtual bool is_renderable() const
Returns true if there is some value to visiting this particular node during the cull traversal for an...
Definition: ropeNode.cxx:201