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