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  */
101 bool RopeNode::
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  */
124 bool RopeNode::
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  */
171 bool RopeNode::
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  */
204 void RopeNode::
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  */
870 void RopeNode::
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  */
879 void RopeNode::
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const TransformState * get_camera_transform() const
Returns the position of the camera relative to the starting node.
Definition: cullTraverser.I:61
Indicates a coordinate-system transform on vertices.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
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:3589
CPT(GeomVertexFormat) RopeNode
Returns the appropriate GeomVertexFormat for rendering, according to the user-specified requirements.
Definition: ropeNode.cxx:234
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:695
get_thickness
Returns the thickness of the rope.
Definition: ropeNode.h:153
get_render_mode
Returns the method used to render the rope.
Definition: ropeNode.h:141
This defines a bounding sphere, consisting of a center and a radius.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
This class is an abstraction for evaluating NURBS curves.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
void skip_pointer(DatagramIterator &scan)
Reads and discards a pointer value from the Bam file.
Definition: bamReader.cxx:665
This collects together the pieces of data that are accumulated for each node while walking the scene ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class draws a visible representation of the NURBS curve stored in its NurbsCurveEvaluator.
Definition: ropeNode.h:34
Defines a series of triangle strips.
Definition: geomTristrips.h:23
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
get_vertex_color_dimension
Returns the numeric extended dimension in which the color components should be found.
Definition: ropeNode.h:148
get_pipeline_stage
Returns the Pipeline stage number associated with this thread.
Definition: thread.h:105
get_num_subdiv
Returns the number of subdivisions per cubic segment to draw.
Definition: ropeNode.h:149
get_mat
Returns the matrix that describes the transform.
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...
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
get_uv_mode
Returns the algorithm to use to generate UV's for the rope.
Definition: ropeNode.h:142
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 ...
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
get_matrix
Returns the optional matrix which is used to transform each control vertex after it has been transfor...
Definition: ropeNode.h:154
get_normal_mode
Returns the kind of normals to generate for the rope.
Definition: ropeNode.h:145
has_matrix
Returns true if the node has a matrix set, false otherwise.
Definition: ropeNode.h:154
get_vertex_thickness_dimension
Returns the numeric extended dimension in which the thickness component should be found.
Definition: ropeNode.h:152
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The smallest atom of cull.
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
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
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:54
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
get_use_vertex_thickness
Returns the "use vertex thickness" flag.
Definition: ropeNode.h:151
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
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).
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_num_rows() const
Returns the number of rows stored within all the arrays.
get_curve
Returns the curve represented by the RopeNode.
Definition: ropeNode.h:140
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:47
get_num_slices
Returns the number of radial subdivisions to make if RenderMode is RM_tube.
Definition: ropeNode.h:150
Defines a series of disconnected line segments.
Definition: geomLines.h:23
This class defines the physical layout of the vertex data stored within a Geom.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void register_with_read_factory()
Tells the BamReader how to create objects of type RopeNode.
Definition: ropeNode.cxx:871
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
void reset_bound(const NodePath &rel_to)
Recomputes the bounding volume.
Definition: ropeNode.cxx:205
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A thread; that is, a lightweight process.
Definition: thread.h:46
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition: ropeNode.cxx:92
This describes the structure of a single array within a Geom data.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
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:880
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
get_tube_up
Returns the normal vector used to control the "top" of the curve, when RenderMode is RM_tube.
Definition: ropeNode.h:146
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
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:161
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_use_vertex_color
Returns the "use vertex color" flag.
Definition: ropeNode.h:147
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
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
get_uv_scale
Returns the scaling factor to apply to generated UV's for the rope.
Definition: ropeNode.h:144