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 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 */
587void RopeNode::
588compute_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 */
632void RopeNode::
633compute_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 */
698void RopeNode::
699compute_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 */
799void RopeNode::
800compute_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 */
838PN_stdfloat RopeNode::
839compute_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 */
880write_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 */
890TypedWritable *RopeNode::
891make_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 */
906void RopeNode::
907fillin(DatagramIterator &scan, BamReader *manager) {
908 PandaNode::fillin(scan, manager);
909 manager->read_cdata(scan, _cycler);
910}
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:871
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:880
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.