Panda3D
Loading...
Searching...
No Matches
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
33TypeHandle RopeNode::_type_handle;
34
35PStatCollector RopeNode::_rope_node_pcollector("*:RopeNode");
36
37/**
38 *
39 */
40CycleData *RopeNode::CData::
41make_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 */
49void RopeNode::CData::
50write_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 */
60void RopeNode::CData::
61fillin(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 */
69RopeNode::
70RopeNode(const std::string &name) :
71 PandaNode(name)
72{
73 set_cull_callback();
74}
75
76/**
77 *
78 */
79RopeNode::
80RopeNode(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 */
92make_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 */
102safe_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) {
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 */
172is_renderable() const {
173 return true;
174}
175
176/**
177 *
178 */
179void RopeNode::
180output(std::ostream &out) const {
181 PandaNode::output(out);
183 if (curve != nullptr) {
184 out << " " << *curve;
185 } else {
186 out << " (no curve)";
187 }
188}
189
190/**
191 *
192 */
193void RopeNode::
194write(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 */
205reset_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 */
217void RopeNode::
218compute_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 */
234CPT(GeomVertexFormat) RopeNode::
235get_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 */
262PT(BoundingVolume) RopeNode::
263do_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
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 */
303void RopeNode::
304render_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 */
348void RopeNode::
349render_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 */
394void RopeNode::
395render_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 */
447void RopeNode::
448render_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 */
512int RopeNode::
513get_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 // We need a bit more relaxed threshold to prevent breaks between
530 // segments, see GitHub issue #1325.
531#ifdef STDFLOAT_DOUBLE
532 static const double threshold = 1.0e-8;
533#else
534 static const float threshold = 1.0e-4f;
535#endif
536
537 if (curve_segment == nullptr ||
538 !point.almost_equal(last_point, threshold)) {
539 // If the first point of this segment is different from the last point
540 // of the previous segment, end the previous segment and begin a new
541 // one.
542 curve_segments.push_back(CurveSegment());
543 curve_segment = &curve_segments.back();
544
545 CurveVertex vtx;
546 vtx._p = point;
547 vtx._t = result->get_segment_t(segment, 0.0f);
548 if (use_vertex_color) {
549 result->eval_segment_extended_points(segment, 0.0f,
551 &vtx._c[0], 4);
552 }
553 if (use_vertex_thickness) {
554 vtx._thickness =
555 result->eval_segment_extended_point(segment, 0.0f,
557 }
558
559 curve_segment->push_back(vtx);
560 ++num_curve_verts;
561 }
562
563 // Store all the remaining points in this segment.
564 for (int i = 1; i < num_verts; ++i) {
565 PN_stdfloat t = (PN_stdfloat)i / (PN_stdfloat)(num_verts - 1);
566
567 CurveVertex vtx;
568 result->eval_segment_point(segment, t, vtx._p);
569 vtx._t = result->get_segment_t(segment, t);
570 if (use_vertex_color) {
571 result->eval_segment_extended_points(segment, t,
573 &vtx._c[0], 4);
574 }
575 if (use_vertex_thickness) {
576 vtx._thickness =
577 result->eval_segment_extended_point(segment, t,
579 }
580
581 curve_segment->push_back(vtx);
582 ++num_curve_verts;
583
584 last_point = vtx._p;
585 }
586 }
587
588 return num_curve_verts;
589}
590
591/**
592 * Calculates the vertices for a RM_thread render. This just copies the
593 * vertices more-or-less directly into the array.
594 */
595void RopeNode::
596compute_thread_vertices(GeomVertexData *vdata,
597 const RopeNode::CurveSegments &curve_segments,
598 int num_curve_verts) const {
599 vdata->set_num_rows(num_curve_verts);
600
601 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
602 GeomVertexWriter color(vdata, InternalName::get_color());
603 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
604
605 UVMode uv_mode = get_uv_mode();
606 PN_stdfloat uv_scale = get_uv_scale();
607 bool u_dominant = get_uv_direction();
608 bool use_vertex_color = get_use_vertex_color();
609
610 PN_stdfloat dist = 0.0f;
611 CurveSegments::const_iterator si;
612 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
613 const CurveSegment &segment = (*si);
614 for (size_t j = 0; j < segment.size(); ++j) {
615 vertex.add_data3(segment[j]._p);
616
617 if (use_vertex_color) {
618 color.add_data4(segment[j]._c);
619 }
620
621 PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
622
623 if (uv_mode != UV_none) {
624 if (u_dominant) {
625 texcoord.add_data2(uv_t, 0.0f);
626 } else {
627 texcoord.add_data2(0.0f, uv_t);
628 }
629 }
630 }
631 }
632
633 nassertv(vdata->get_num_rows() == num_curve_verts);
634}
635
636/**
637 * Calculates the vertices for a RM_billboard render. This puts a pair of
638 * vertices on either side of each computed point in curve_segments.
639 */
640void RopeNode::
641compute_billboard_vertices(GeomVertexData *vdata,
642 const LVector3 &camera_vec,
643 const RopeNode::CurveSegments &curve_segments,
644 int num_curve_verts,
645 NurbsCurveResult *result) const {
646 int expected_num_verts = num_curve_verts * 2;
647 vdata->set_num_rows(expected_num_verts);
648
649 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
650 GeomVertexWriter color(vdata, InternalName::get_color());
651 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
652
653 PN_stdfloat thickness = get_thickness();
654 PN_stdfloat overall_radius = thickness * 0.5f;
655 PN_stdfloat radius = overall_radius;
656 UVMode uv_mode = get_uv_mode();
657 PN_stdfloat uv_scale = get_uv_scale();
658 bool u_dominant = get_uv_direction();
659 bool use_vertex_color = get_use_vertex_color();
660 bool use_vertex_thickness = get_use_vertex_thickness();
661
662 PN_stdfloat dist = 0.0f;
663 CurveSegments::const_iterator si;
664 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
665 const CurveSegment &segment = (*si);
666 for (size_t j = 0; j < segment.size(); ++j) {
667 LVector3 tangent;
668 compute_tangent(tangent, segment, j, result);
669
670 LVector3 norm = cross(tangent, camera_vec);
671 norm.normalize();
672
673 if (use_vertex_thickness) {
674 radius = overall_radius * segment[j]._thickness;
675 }
676
677 vertex.add_data3(segment[j]._p + norm * radius);
678 vertex.add_data3(segment[j]._p - norm * radius);
679
680 if (use_vertex_color) {
681 color.add_data4(segment[j]._c);
682 color.add_data4(segment[j]._c);
683 }
684
685 PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
686
687 if (uv_mode != UV_none) {
688 if (u_dominant) {
689 texcoord.add_data2(uv_t, 1.0f);
690 texcoord.add_data2(uv_t, 0.0f);
691 } else {
692 texcoord.add_data2(1.0f, uv_t);
693 texcoord.add_data2(0.0f, uv_t);
694 }
695 }
696 }
697 }
698
699 nassertv(vdata->get_num_rows() == expected_num_verts);
700}
701
702/**
703 * Calculates the vertices for a RM_tube render. This puts a ring of vertices
704 * around each computed point in curve_segments.
705 */
706void RopeNode::
707compute_tube_vertices(GeomVertexData *vdata,
708 int &num_verts_per_slice,
709 const RopeNode::CurveSegments &curve_segments,
710 int num_curve_verts,
711 NurbsCurveResult *result) const {
712 int num_slices = get_num_slices();
713 num_verts_per_slice = num_slices;
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 NormalMode normal_mode = get_normal_mode();
722 bool use_vertex_color = get_use_vertex_color();
723 bool use_vertex_thickness = get_use_vertex_thickness();
724
725 // If we are generating UV's, we will need to duplicate the vertices along
726 // the seam so that the UV's go through the whole range of 0..1 instead of
727 // reflecting in the last polygon before the seam.
728 if (uv_mode != UV_none) {
729 ++num_verts_per_slice;
730 }
731
732 int expected_num_verts = num_curve_verts * num_verts_per_slice;
733 vdata->set_num_rows(expected_num_verts);
734
735 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
736 GeomVertexWriter normal(vdata, InternalName::get_normal());
737 GeomVertexWriter color(vdata, InternalName::get_color());
738 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
739
740 LVector3 up = get_tube_up();
741
742 PN_stdfloat dist = 0.0f;
743 CurveSegments::const_iterator si;
744 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) {
745 const CurveSegment &segment = (*si);
746 for (size_t j = 0; j < segment.size(); ++j) {
747 LVector3 tangent;
748 compute_tangent(tangent, segment, j, result);
749
750 LVector3 norm = cross(tangent, up);
751
752 // In case the tangent is linear dependent on the up vector, we might
753 // get invalid results, so check that
754 if (IS_NEARLY_ZERO(norm.length_squared())) {
755
756 if (IS_NEARLY_ZERO(tangent.get_y()) && IS_NEARLY_ZERO(tangent.get_z())) {
757 // Vector is linear dependent on (1, 0, 0), use (0, 1, 0) as base
758 norm = cross(tangent, LVector3(0, 1, 0));
759 } else {
760 norm = cross(tangent, LVector3(1, 0, 0));
761 }
762 }
763
764 norm.normalize();
765 up = cross(norm, tangent);
766
767 LMatrix3 rotate = LMatrix3::rotate_mat(360.0f / (PN_stdfloat)num_slices,
768 tangent);
769
770 PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j);
771
772 for (int s = 0; s < num_verts_per_slice; ++s) {
773 if (use_vertex_thickness) {
774 radius = overall_radius * segment[j]._thickness;
775 }
776
777 vertex.add_data3(segment[j]._p + norm * radius);
778
779 if (normal_mode == NM_vertex) {
780 normal.add_data3(norm);
781 }
782
783 if (use_vertex_color) {
784 color.add_data4(segment[j]._c);
785 }
786
787 norm = norm * rotate;
788
789 if (uv_mode != UV_none) {
790 PN_stdfloat uv_s = (PN_stdfloat)s / (PN_stdfloat)num_slices;
791 if (u_dominant) {
792 texcoord.add_data2(uv_t, uv_s);
793 } else {
794 texcoord.add_data2(uv_s, uv_t);
795 }
796 }
797 }
798 }
799 }
800
801 nassertv(vdata->get_num_rows() == expected_num_verts);
802}
803
804/**
805 * Computes the tangent to the curve at the indicated point in the segment.
806 */
807void RopeNode::
808compute_tangent(LVector3 &tangent, const RopeNode::CurveSegment &segment,
809 size_t j, NurbsCurveResult *result) {
810 // First, try to evaluate the tangent at the curve. This gives better
811 // results at the ends at the endpoints where the tangent does not go to
812 // zero.
813
814 /*
815 Actually, on second thought this looks terrible.
816
817 if (result->eval_tangent(segment[j]._t, tangent)) {
818 if (!tangent.almost_equal(LVector3::zero())) {
819 return;
820 }
821 }
822 */
823
824 // If that failed (or produced a zero tangent), then derive the tangent from
825 // the neighboring points instead.
826 if (j == 0) {
827 tangent = segment[j + 1]._p - segment[j]._p;
828 } else if (j == segment.size() - 1) {
829 tangent = segment[j]._p - segment[j - 1]._p;
830 } else {
831 tangent = segment[j + 1]._p - segment[j - 1]._p;
832 }
833
834 // Avoid empty tangents, these lead to crashes. Instead, use an arbitrary
835 // tangent.
836 if (IS_NEARLY_ZERO(tangent.length_squared())) {
837 tangent.set(0, 0, 1);
838 }
839
840}
841
842/**
843 * Computes the texture coordinate along the curve for the indicated point in
844 * the segment.
845 */
846PN_stdfloat RopeNode::
847compute_uv_t(PN_stdfloat &dist, const RopeNode::UVMode &uv_mode,
848 PN_stdfloat uv_scale, const RopeNode::CurveSegment &segment,
849 size_t j) {
850 switch (uv_mode) {
851 case UV_none:
852 return 0.0f;
853
854 case UV_parametric:
855 return segment[j]._t * uv_scale;
856
857 case UV_distance:
858 if (j != 0) {
859 LVector3 vec = segment[j]._p - segment[j - 1]._p;
860 dist += vec.length();
861 }
862 return dist * uv_scale;
863
864 case UV_distance2:
865 if (j != 0) {
866 LVector3 vec = segment[j]._p - segment[j - 1]._p;
867 dist += vec.length_squared();
868 }
869 return dist * uv_scale;
870 }
871
872 return 0.0f;
873}
874
875/**
876 * Tells the BamReader how to create objects of type RopeNode.
877 */
880 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
881}
882
883/**
884 * Writes the contents of this object to the datagram for shipping out to a
885 * Bam file.
886 */
888write_datagram(BamWriter *manager, Datagram &dg) {
889 PandaNode::write_datagram(manager, dg);
890 manager->write_cdata(dg, _cycler);
891}
892
893/**
894 * This function is called by the BamReader's factory when a new object of
895 * type RopeNode is encountered in the Bam file. It should create the
896 * RopeNode and extract its information from the file.
897 */
898TypedWritable *RopeNode::
899make_from_bam(const FactoryParams &params) {
900 RopeNode *node = new RopeNode("");
901 DatagramIterator scan;
902 BamReader *manager;
903
904 parse_params(params, scan, manager);
905 node->fillin(scan, manager);
906
907 return node;
908}
909
910/**
911 * This internal function is called by make_from_bam to read in all of the
912 * relevant data from the BamFile for the new RopeNode.
913 */
914void RopeNode::
915fillin(DatagramIterator &scan, BamReader *manager) {
916 PandaNode::fillin(scan, manager);
917 manager->read_cdata(scan, _cycler);
918}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 skip_pointer(DatagramIterator &scan)
Reads and discards a pointer value from the Bam file.
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
This defines a bounding sphere, consisting of a center and a radius.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
virtual void record_object(CullableObject *object, const CullTraverser *traverser)
This callback function is intended to be overridden by a derived class.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
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.
The smallest atom of cull.
A single page of data maintained by a PipelineCycler.
Definition cycleData.h:50
A class to retrieve the individual data elements previously stored in a Datagram.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
An instance of this class is passed to the Factory when requesting it to do its business and construc...
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
Defines a series of disconnected line segments.
Definition geomLines.h:23
Defines a series of triangle strips.
This describes the structure of a single array within a Geom data.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
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.
This class defines the physical layout of the vertex data stored within a Geom.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition geom.h:54
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
bool around(const GeometricBoundingVolume **first, const GeometricBoundingVolume **last)
Resets the volume to enclose only the volumes indicated.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
This class is an abstraction for evaluating NURBS curves.
The result of a NurbsCurveEvaluator.
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...
int get_num_segments() const
Returns the number of piecewise continuous segments within the curve.
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.
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...
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...
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition pStatTimer.h:30
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
This class draws a visible representation of the NURBS curve stored in its NurbsCurveEvaluator.
Definition ropeNode.h:34
get_num_slices
Returns the number of radial subdivisions to make if RenderMode is RM_tube.
Definition ropeNode.h:150
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
get_vertex_thickness_dimension
Returns the numeric extended dimension in which the thickness component should be found.
Definition ropeNode.h:152
get_render_mode
Returns the method used to render the rope.
Definition ropeNode.h:141
void reset_bound(const NodePath &rel_to)
Recomputes the bounding volume.
Definition ropeNode.cxx:205
get_thickness
Returns the thickness of the rope.
Definition ropeNode.h:153
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
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_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_uv_mode
Returns the algorithm to use to generate UV's for the rope.
Definition ropeNode.h:142
get_num_subdiv
Returns the number of subdivisions per cubic segment to draw.
Definition ropeNode.h:149
get_uv_scale
Returns the scaling factor to apply to generated UV's for the rope.
Definition ropeNode.h:144
get_use_vertex_color
Returns the "use vertex color" flag.
Definition ropeNode.h:147
static void register_with_read_factory()
Tells the BamReader how to create objects of type RopeNode.
Definition ropeNode.cxx:879
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
get_tube_up
Returns the normal vector used to control the "top" of the curve, when RenderMode is RM_tube.
Definition ropeNode.h:146
get_vertex_color_dimension
Returns the numeric extended dimension in which the color components should be found.
Definition ropeNode.h:148
get_use_vertex_thickness
Returns the "use vertex thickness" flag.
Definition ropeNode.h:151
get_matrix
Returns the optional matrix which is used to transform each control vertex after it has been transfor...
Definition ropeNode.h:154
get_curve
Returns the curve represented by the RopeNode.
Definition ropeNode.h:140
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:888
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition ropeNode.cxx:92
A thread; that is, a lightweight process.
Definition thread.h:46
get_pipeline_stage
Returns the Pipeline stage number associated with this thread.
Definition thread.h:105
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
Indicates a coordinate-system transform on vertices.
get_mat
Returns the matrix that describes the transform.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.