Panda3D
|
00001 // Filename: ropeNode.cxx 00002 // Created by: drose (04Dec02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "ropeNode.h" 00016 #include "cullTraverser.h" 00017 #include "cullTraverserData.h" 00018 #include "cullableObject.h" 00019 #include "cullHandler.h" 00020 #include "renderState.h" 00021 #include "renderModeAttrib.h" 00022 #include "colorAttrib.h" 00023 #include "bamWriter.h" 00024 #include "bamReader.h" 00025 #include "datagram.h" 00026 #include "datagramIterator.h" 00027 #include "pStatTimer.h" 00028 #include "geom.h" 00029 #include "geomLines.h" 00030 #include "geomTristrips.h" 00031 #include "geomVertexWriter.h" 00032 #include "boundingSphere.h" 00033 00034 TypeHandle RopeNode::_type_handle; 00035 00036 PStatCollector RopeNode::_rope_node_pcollector("*:RopeNode"); 00037 00038 //////////////////////////////////////////////////////////////////// 00039 // Function: RopeNode::CData::make_copy 00040 // Access: Public, Virtual 00041 // Description: 00042 //////////////////////////////////////////////////////////////////// 00043 CycleData *RopeNode::CData:: 00044 make_copy() const { 00045 return new CData(*this); 00046 } 00047 00048 //////////////////////////////////////////////////////////////////// 00049 // Function: RopeNode::CData::write_datagram 00050 // Access: Public, Virtual 00051 // Description: Writes the contents of this object to the datagram 00052 // for shipping out to a Bam file. 00053 //////////////////////////////////////////////////////////////////// 00054 void RopeNode::CData:: 00055 write_datagram(BamWriter *writer, Datagram &dg) const { 00056 // For now, we write a NULL pointer. Eventually we will write out 00057 // the NurbsCurveEvaluator pointer. 00058 writer->write_pointer(dg, (TypedWritable *)NULL); 00059 } 00060 00061 //////////////////////////////////////////////////////////////////// 00062 // Function: RopeNode::CData::fillin 00063 // Access: Public, Virtual 00064 // Description: This internal function is called by make_from_bam to 00065 // read in all of the relevant data from the BamFile for 00066 // the new RopeNode. 00067 //////////////////////////////////////////////////////////////////// 00068 void RopeNode::CData:: 00069 fillin(DatagramIterator &scan, BamReader *reader) { 00070 // For now, we skip over the NULL pointer that we wrote out. 00071 reader->skip_pointer(scan); 00072 } 00073 00074 //////////////////////////////////////////////////////////////////// 00075 // Function: RopeNode::Constructor 00076 // Access: Public 00077 // Description: 00078 //////////////////////////////////////////////////////////////////// 00079 RopeNode:: 00080 RopeNode(const string &name) : 00081 PandaNode(name) 00082 { 00083 set_cull_callback(); 00084 } 00085 00086 //////////////////////////////////////////////////////////////////// 00087 // Function: RopeNode::Copy Constructor 00088 // Access: Protected 00089 // Description: 00090 //////////////////////////////////////////////////////////////////// 00091 RopeNode:: 00092 RopeNode(const RopeNode ©) : 00093 PandaNode(copy), 00094 _cycler(copy._cycler) 00095 { 00096 } 00097 00098 //////////////////////////////////////////////////////////////////// 00099 // Function: RopeNode::make_copy 00100 // Access: Public, Virtual 00101 // Description: Returns a newly-allocated Node that is a shallow copy 00102 // of this one. It will be a different Node pointer, 00103 // but its internal data may or may not be shared with 00104 // that of the original Node. 00105 //////////////////////////////////////////////////////////////////// 00106 PandaNode *RopeNode:: 00107 make_copy() const { 00108 return new RopeNode(*this); 00109 } 00110 00111 //////////////////////////////////////////////////////////////////// 00112 // Function: RopeNode::safe_to_transform 00113 // Access: Public, Virtual 00114 // Description: Returns true if it is generally safe to transform 00115 // this particular kind of Node by calling the xform() 00116 // method, false otherwise. For instance, it's usually 00117 // a bad idea to attempt to xform a RopeNode. 00118 //////////////////////////////////////////////////////////////////// 00119 bool RopeNode:: 00120 safe_to_transform() const { 00121 return false; 00122 } 00123 00124 //////////////////////////////////////////////////////////////////// 00125 // Function: RopeNode::cull_callback 00126 // Access: Public, Virtual 00127 // Description: This function will be called during the cull 00128 // traversal to perform any additional operations that 00129 // should be performed at cull time. This may include 00130 // additional manipulation of render state or additional 00131 // visible/invisible decisions, or any other arbitrary 00132 // operation. 00133 // 00134 // Note that this function will *not* be called unless 00135 // set_cull_callback() is called in the constructor of 00136 // the derived class. It is necessary to call 00137 // set_cull_callback() to indicated that we require 00138 // cull_callback() to be called. 00139 // 00140 // By the time this function is called, the node has 00141 // already passed the bounding-volume test for the 00142 // viewing frustum, and the node's transform and state 00143 // have already been applied to the indicated 00144 // CullTraverserData object. 00145 // 00146 // The return value is true if this node should be 00147 // visible, or false if it should be culled. 00148 //////////////////////////////////////////////////////////////////// 00149 bool RopeNode:: 00150 cull_callback(CullTraverser *trav, CullTraverserData &data) { 00151 // Statistics 00152 PStatTimer timer(_rope_node_pcollector); 00153 00154 // Create some geometry on-the-fly to render the rope. 00155 if (get_num_subdiv() > 0) { 00156 NurbsCurveEvaluator *curve = get_curve(); 00157 if (curve != (NurbsCurveEvaluator *)NULL) { 00158 PT(NurbsCurveResult) result; 00159 if (has_matrix()) { 00160 result = curve->evaluate(data._node_path.get_node_path(), get_matrix()); 00161 } else { 00162 result = curve->evaluate(data._node_path.get_node_path()); 00163 } 00164 00165 if (result->get_num_segments() > 0) { 00166 switch (get_render_mode()) { 00167 case RM_thread: 00168 render_thread(trav, data, result); 00169 break; 00170 00171 case RM_tape: 00172 render_tape(trav, data, result); 00173 break; 00174 00175 case RM_billboard: 00176 render_billboard(trav, data, result); 00177 break; 00178 00179 case RM_tube: 00180 render_tube(trav, data, result); 00181 break; 00182 } 00183 } 00184 } 00185 } 00186 00187 return true; 00188 } 00189 00190 //////////////////////////////////////////////////////////////////// 00191 // Function: RopeNode::is_renderable 00192 // Access: Public, Virtual 00193 // Description: Returns true if there is some value to visiting this 00194 // particular node during the cull traversal for any 00195 // camera, false otherwise. This will be used to 00196 // optimize the result of get_net_draw_show_mask(), so 00197 // that any subtrees that contain only nodes for which 00198 // is_renderable() is false need not be visited. 00199 //////////////////////////////////////////////////////////////////// 00200 bool RopeNode:: 00201 is_renderable() const { 00202 return true; 00203 } 00204 00205 //////////////////////////////////////////////////////////////////// 00206 // Function: RopeNode::output 00207 // Access: Public, Virtual 00208 // Description: 00209 //////////////////////////////////////////////////////////////////// 00210 void RopeNode:: 00211 output(ostream &out) const { 00212 PandaNode::output(out); 00213 NurbsCurveEvaluator *curve = get_curve(); 00214 if (curve != (NurbsCurveEvaluator *)NULL) { 00215 out << " " << *curve; 00216 } else { 00217 out << " (no curve)"; 00218 } 00219 } 00220 00221 //////////////////////////////////////////////////////////////////// 00222 // Function: RopeNode::write 00223 // Access: Public, Virtual 00224 // Description: 00225 //////////////////////////////////////////////////////////////////// 00226 void RopeNode:: 00227 write(ostream &out, int indent_level) const { 00228 PandaNode::write(out, indent_level); 00229 indent(out, indent_level) << *get_curve() << "\n"; 00230 } 00231 00232 //////////////////////////////////////////////////////////////////// 00233 // Function: RopeNode::reset_bound 00234 // Access: Published 00235 // Description: Recomputes the bounding volume. This is normally 00236 // called automatically, but it must occasionally be 00237 // called explicitly when the curve has changed 00238 // properties outside of this node's knowledge. 00239 //////////////////////////////////////////////////////////////////// 00240 void RopeNode:: 00241 reset_bound(const NodePath &rel_to) { 00242 Thread *current_thread = Thread::get_current_thread(); 00243 int pipeline_stage = current_thread->get_pipeline_stage(); 00244 do_recompute_bounds(rel_to, pipeline_stage, current_thread); 00245 mark_internal_bounds_stale(current_thread); 00246 } 00247 00248 //////////////////////////////////////////////////////////////////// 00249 // Function: RopeNode::compute_internal_bounds 00250 // Access: Protected, Virtual 00251 // Description: Called when needed to recompute the node's 00252 // _internal_bound object. Nodes that contain anything 00253 // of substance should redefine this to do the right 00254 // thing. 00255 //////////////////////////////////////////////////////////////////// 00256 void RopeNode:: 00257 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, 00258 int &internal_vertices, 00259 int pipeline_stage, 00260 Thread *current_thread) const { 00261 PT(BoundingVolume) bounds = 00262 do_recompute_bounds(NodePath((PandaNode *)this), pipeline_stage, 00263 current_thread); 00264 00265 internal_bounds = bounds; 00266 internal_vertices = 0; // TODO--estimate this better. 00267 } 00268 00269 //////////////////////////////////////////////////////////////////// 00270 // Function: RopeNode::get_format 00271 // Access: Private 00272 // Description: Returns the appropriate GeomVertexFormat for 00273 // rendering, according to the user-specified 00274 // requirements. 00275 //////////////////////////////////////////////////////////////////// 00276 CPT(GeomVertexFormat) RopeNode:: 00277 get_format(bool support_normals) const { 00278 PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat 00279 (InternalName::get_vertex(), 3, Geom::NT_stdfloat, 00280 Geom::C_point); 00281 00282 if (support_normals && get_normal_mode() == NM_vertex) { 00283 array_format->add_column 00284 (InternalName::get_normal(), 3, Geom::NT_stdfloat, 00285 Geom::C_vector); 00286 } 00287 if (get_use_vertex_color()) { 00288 array_format->add_column 00289 (InternalName::get_color(), 1, Geom::NT_packed_dabc, 00290 Geom::C_color); 00291 } 00292 if (get_uv_mode() != UV_none) { 00293 array_format->add_column 00294 (InternalName::get_texcoord(), 2, Geom::NT_stdfloat, 00295 Geom::C_texcoord); 00296 } 00297 00298 return GeomVertexFormat::register_format(array_format); 00299 } 00300 00301 //////////////////////////////////////////////////////////////////// 00302 // Function: RopeNode::do_recompute_bounds 00303 // Access: Private 00304 // Description: Does the actual internal recompute. 00305 //////////////////////////////////////////////////////////////////// 00306 PT(BoundingVolume) RopeNode:: 00307 do_recompute_bounds(const NodePath &rel_to, int pipeline_stage, 00308 Thread *current_thread) const { 00309 // TODO: fix the bounds so that it properly reflects the indicated 00310 // pipeline stage. At the moment, we cheat and get some of the 00311 // properties from the current pipeline stage, the lazy way. 00312 00313 // First, get ourselves a fresh, empty bounding volume. 00314 PT(BoundingVolume) bound = new BoundingSphere; 00315 00316 NurbsCurveEvaluator *curve = get_curve(); 00317 if (curve != (NurbsCurveEvaluator *)NULL) { 00318 NurbsCurveEvaluator::Vert3Array verts; 00319 get_curve()->get_vertices(verts, rel_to); 00320 00321 if (has_matrix()) { 00322 // And then apply the indicated matrix. 00323 const LMatrix4 &mat = get_matrix(); 00324 NurbsCurveEvaluator::Vert3Array::iterator vi; 00325 for (vi = verts.begin(); vi != verts.end(); ++vi) { 00326 (*vi) = LPoint3(*vi) * mat; 00327 } 00328 } 00329 00330 GeometricBoundingVolume *gbv; 00331 DCAST_INTO_R(gbv, bound, bound); 00332 gbv->around(&verts[0], &verts[0] + verts.size()); 00333 } 00334 return bound; 00335 } 00336 00337 //////////////////////////////////////////////////////////////////// 00338 // Function: RopeNode::render_thread 00339 // Access: Private 00340 // Description: Draws the rope in RM_thread mode. This uses a 00341 // GeomLinestrip to draw the rope in the simplest 00342 // possible method, generally resulting in a 00343 // one-pixel-wide curve. 00344 // 00345 // In this mode, the thickness parameter represents a 00346 // thickness in pixels, and is passed to the linestrip. 00347 // However, you should be aware the DirectX does not 00348 // support line thickness. This mode does not support 00349 // per-vertex thickness. 00350 //////////////////////////////////////////////////////////////////// 00351 void RopeNode:: 00352 render_thread(CullTraverser *trav, CullTraverserData &data, 00353 NurbsCurveResult *result) const { 00354 CurveSegments curve_segments; 00355 int num_curve_verts = get_connected_segments(curve_segments, result); 00356 00357 // Now we have stored one or more sequences of vertices down the 00358 // thread. These map directly to primitive vertices. 00359 PT(GeomVertexData) vdata = new GeomVertexData 00360 ("rope", get_format(false), Geom::UH_stream); 00361 compute_thread_vertices(vdata, curve_segments, num_curve_verts); 00362 00363 // We use GeomLines instead of GeomLinestrips, since that can more 00364 // easily be rendered directly. 00365 PT(GeomLines) lines = new GeomLines(Geom::UH_stream); 00366 lines->reserve_num_vertices((num_curve_verts - 1) * 2); 00367 00368 for (int vi = 0; vi < num_curve_verts - 1; ++vi) { 00369 lines->add_vertex(vi); 00370 lines->add_vertex(vi + 1); 00371 lines->close_primitive(); 00372 } 00373 00374 PT(Geom) geom = new Geom(vdata); 00375 geom->add_primitive(lines); 00376 00377 CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, get_thickness()); 00378 CPT(RenderState) state = data._state->add_attrib(thick); 00379 if (get_use_vertex_color()) { 00380 state = state->add_attrib(ColorAttrib::make_vertex()); 00381 } 00382 00383 CullableObject *object = 00384 new CullableObject(geom, state, 00385 data.get_net_transform(trav), 00386 data.get_modelview_transform(trav), 00387 trav->get_gsg()); 00388 trav->get_cull_handler()->record_object(object, trav); 00389 } 00390 00391 //////////////////////////////////////////////////////////////////// 00392 // Function: RopeNode::render_tape 00393 // Access: Private 00394 // Description: Draws the rope in RM_tape mode. This draws a 00395 // series of triangle strips oriented to be 00396 // perpendicular to the tube_up vector. 00397 // 00398 // In this mode, thickness is in spatial units, and 00399 // determines the width of the triangle strips. 00400 //////////////////////////////////////////////////////////////////// 00401 void RopeNode:: 00402 render_tape(CullTraverser *trav, CullTraverserData &data, 00403 NurbsCurveResult *result) const { 00404 CurveSegments curve_segments; 00405 int num_curve_verts = get_connected_segments(curve_segments, result); 00406 00407 // Now we have stored one or more sequences of vertices down the 00408 // center strips. Go back through and calculate the vertices on 00409 // either side. 00410 PT(GeomVertexData) vdata = new GeomVertexData 00411 ("rope", get_format(false), Geom::UH_stream); 00412 00413 compute_billboard_vertices(vdata, -get_tube_up(), 00414 curve_segments, num_curve_verts, result); 00415 00416 // Since this will be a nonindexed primitive, no need to pre-reserve 00417 // the number of vertices. 00418 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); 00419 CurveSegments::const_iterator si; 00420 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00421 const CurveSegment &segment = (*si); 00422 00423 strip->add_next_vertices(segment.size() * 2); 00424 strip->close_primitive(); 00425 } 00426 00427 PT(Geom) geom = new Geom(vdata); 00428 geom->add_primitive(strip); 00429 00430 CPT(RenderState) state = data._state; 00431 if (get_use_vertex_color()) { 00432 state = state->add_attrib(ColorAttrib::make_vertex()); 00433 } 00434 00435 CullableObject *object = 00436 new CullableObject(geom, state, 00437 data.get_net_transform(trav), 00438 data.get_modelview_transform(trav), 00439 trav->get_gsg()); 00440 trav->get_cull_handler()->record_object(object, trav); 00441 } 00442 00443 //////////////////////////////////////////////////////////////////// 00444 // Function: RopeNode::render_billboard 00445 // Access: Private 00446 // Description: Draws the rope in RM_billboard mode. This draws a 00447 // series of triangle strips oriented to be 00448 // perpendicular to the camera plane. 00449 // 00450 // In this mode, thickness is in spatial units, and 00451 // determines the width of the triangle strips. 00452 //////////////////////////////////////////////////////////////////// 00453 void RopeNode:: 00454 render_billboard(CullTraverser *trav, CullTraverserData &data, 00455 NurbsCurveResult *result) const { 00456 const TransformState *net_transform = data.get_net_transform(trav); 00457 const TransformState *camera_transform = trav->get_camera_transform(); 00458 00459 CPT(TransformState) rel_transform = 00460 net_transform->invert_compose(camera_transform); 00461 LVector3 camera_vec = LVector3::forward() * rel_transform->get_mat(); 00462 00463 CurveSegments curve_segments; 00464 int num_curve_verts = get_connected_segments(curve_segments, result); 00465 00466 // Now we have stored one or more sequences of vertices down the 00467 // center strips. Go back through and calculate the vertices on 00468 // either side. 00469 PT(GeomVertexData) vdata = new GeomVertexData 00470 ("rope", get_format(false), Geom::UH_stream); 00471 00472 compute_billboard_vertices(vdata, camera_vec, 00473 curve_segments, num_curve_verts, result); 00474 00475 // Since this will be a nonindexed primitive, no need to pre-reserve 00476 // the number of vertices. 00477 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); 00478 CurveSegments::const_iterator si; 00479 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00480 const CurveSegment &segment = (*si); 00481 00482 strip->add_next_vertices(segment.size() * 2); 00483 strip->close_primitive(); 00484 } 00485 00486 PT(Geom) geom = new Geom(vdata); 00487 geom->add_primitive(strip); 00488 00489 CPT(RenderState) state = data._state; 00490 if (get_use_vertex_color()) { 00491 state = state->add_attrib(ColorAttrib::make_vertex()); 00492 } 00493 00494 CullableObject *object = 00495 new CullableObject(geom, state, 00496 data.get_net_transform(trav), 00497 data.get_modelview_transform(trav), 00498 trav->get_gsg()); 00499 trav->get_cull_handler()->record_object(object, trav); 00500 } 00501 00502 //////////////////////////////////////////////////////////////////// 00503 // Function: RopeNode::render_tube 00504 // Access: Private 00505 // Description: Draws the rope in RM_tube mode. This draws a hollow 00506 // tube centered around the string. 00507 // 00508 // In this mode, thickness is in spatial units, and 00509 // determines the diameter of the tube. 00510 //////////////////////////////////////////////////////////////////// 00511 void RopeNode:: 00512 render_tube(CullTraverser *trav, CullTraverserData &data, 00513 NurbsCurveResult *result) const { 00514 CurveSegments curve_segments; 00515 int num_curve_verts = get_connected_segments(curve_segments, result); 00516 00517 // Now, we build up a table of vertices, in a series of rings 00518 // around the circumference of the tube. 00519 00520 int num_slices = get_num_slices(); 00521 int num_verts_per_slice; 00522 00523 PT(GeomVertexData) vdata = new GeomVertexData 00524 ("rope", get_format(true), Geom::UH_stream); 00525 00526 compute_tube_vertices(vdata, num_verts_per_slice, 00527 curve_segments, num_curve_verts, result); 00528 00529 // Finally, go through and build up the index array, to tie all the 00530 // triangle strips together. This is difficult to pre-calculate the 00531 // number of vertices we'll use, so we'll just let it dynamically 00532 // allocate. 00533 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); 00534 int vi = 0; 00535 CurveSegments::const_iterator si; 00536 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00537 const CurveSegment &segment = (*si); 00538 00539 for (int s = 0; s < num_slices; ++s) { 00540 int s1 = (s + 1) % num_verts_per_slice; 00541 00542 for (size_t j = 0; j < segment.size(); ++j) { 00543 strip->add_vertex((vi + j) * num_verts_per_slice + s); 00544 strip->add_vertex((vi + j) * num_verts_per_slice + s1); 00545 } 00546 00547 strip->close_primitive(); 00548 } 00549 vi += (int)segment.size(); 00550 } 00551 00552 PT(Geom) geom = new Geom(vdata); 00553 geom->add_primitive(strip); 00554 00555 CPT(RenderState) state = data._state; 00556 if (get_use_vertex_color()) { 00557 state = state->add_attrib(ColorAttrib::make_vertex()); 00558 } 00559 00560 CullableObject *object = 00561 new CullableObject(geom, state, 00562 data.get_net_transform(trav), 00563 data.get_modelview_transform(trav), 00564 trav->get_gsg()); 00565 trav->get_cull_handler()->record_object(object, trav); 00566 } 00567 00568 //////////////////////////////////////////////////////////////////// 00569 // Function: RopeNode::get_connected_segments 00570 // Access: Private 00571 // Description: Evaluates the string of vertices along the curve, and 00572 // also breaks them up into connected segments. 00573 // 00574 // Since the NurbsCurveEvaluator describes the curve as 00575 // a sequence of possibly-connected piecewise continuous 00576 // segments, this means joining together some adjacent 00577 // segments from the NurbsCurveEvaluator into a single 00578 // CurveSegment, if they happen to be connected (as most 00579 // will be). 00580 // 00581 // The return value is the total number of points across 00582 // all segments. 00583 //////////////////////////////////////////////////////////////////// 00584 int RopeNode:: 00585 get_connected_segments(RopeNode::CurveSegments &curve_segments, 00586 const NurbsCurveResult *result) const { 00587 int num_curve_verts = 0; 00588 00589 int num_verts = get_num_subdiv() + 1; 00590 int num_segments = result->get_num_segments(); 00591 bool use_vertex_color = get_use_vertex_color(); 00592 bool use_vertex_thickness = get_use_vertex_thickness(); 00593 00594 CurveSegment *curve_segment = NULL; 00595 LPoint3 last_point; 00596 00597 for (int segment = 0; segment < num_segments; ++segment) { 00598 LPoint3 point; 00599 result->eval_segment_point(segment, 0.0f, point); 00600 00601 if (curve_segment == (CurveSegment *)NULL || 00602 !point.almost_equal(last_point)) { 00603 // If the first point of this segment is different from the last 00604 // point of the previous segment, end the previous segment and 00605 // begin a new one. 00606 curve_segments.push_back(CurveSegment()); 00607 curve_segment = &curve_segments.back(); 00608 00609 CurveVertex vtx; 00610 vtx._p = point; 00611 vtx._t = result->get_segment_t(segment, 0.0f); 00612 if (use_vertex_color) { 00613 result->eval_segment_extended_points(segment, 0.0f, 00614 get_vertex_color_dimension(), 00615 &vtx._c[0], 4); 00616 } 00617 if (use_vertex_thickness) { 00618 vtx._thickness = 00619 result->eval_segment_extended_point(segment, 0.0f, 00620 get_vertex_thickness_dimension()); 00621 } 00622 00623 curve_segment->push_back(vtx); 00624 ++num_curve_verts; 00625 } 00626 00627 // Store all the remaining points in this segment. 00628 for (int i = 1; i < num_verts; ++i) { 00629 PN_stdfloat t = (PN_stdfloat)i / (PN_stdfloat)(num_verts - 1); 00630 00631 CurveVertex vtx; 00632 result->eval_segment_point(segment, t, vtx._p); 00633 vtx._t = result->get_segment_t(segment, t); 00634 if (use_vertex_color) { 00635 result->eval_segment_extended_points(segment, t, 00636 get_vertex_color_dimension(), 00637 &vtx._c[0], 4); 00638 } 00639 if (use_vertex_thickness) { 00640 vtx._thickness = 00641 result->eval_segment_extended_point(segment, t, 00642 get_vertex_thickness_dimension()); 00643 } 00644 00645 curve_segment->push_back(vtx); 00646 ++num_curve_verts; 00647 00648 last_point = vtx._p; 00649 } 00650 } 00651 00652 return num_curve_verts; 00653 } 00654 00655 //////////////////////////////////////////////////////////////////// 00656 // Function: RopeNode::compute_thread_vertices 00657 // Access: Private 00658 // Description: Calculates the vertices for a RM_thread render. This 00659 // just copies the vertices more-or-less directly into 00660 // the array. 00661 //////////////////////////////////////////////////////////////////// 00662 void RopeNode:: 00663 compute_thread_vertices(GeomVertexData *vdata, 00664 const RopeNode::CurveSegments &curve_segments, 00665 int num_curve_verts) const { 00666 vdata->set_num_rows(num_curve_verts); 00667 00668 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00669 GeomVertexWriter color(vdata, InternalName::get_color()); 00670 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); 00671 00672 UVMode uv_mode = get_uv_mode(); 00673 PN_stdfloat uv_scale = get_uv_scale(); 00674 bool u_dominant = get_uv_direction(); 00675 bool use_vertex_color = get_use_vertex_color(); 00676 00677 PN_stdfloat dist = 0.0f; 00678 CurveSegments::const_iterator si; 00679 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00680 const CurveSegment &segment = (*si); 00681 for (size_t j = 0; j < segment.size(); ++j) { 00682 vertex.add_data3(segment[j]._p); 00683 00684 if (use_vertex_color) { 00685 color.add_data4(segment[j]._c); 00686 } 00687 00688 PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j); 00689 00690 if (uv_mode != UV_none) { 00691 if (u_dominant) { 00692 texcoord.add_data2(uv_t, 0.0f); 00693 } else { 00694 texcoord.add_data2(0.0f, uv_t); 00695 } 00696 } 00697 } 00698 } 00699 00700 nassertv(vdata->get_num_rows() == num_curve_verts); 00701 } 00702 00703 //////////////////////////////////////////////////////////////////// 00704 // Function: RopeNode::compute_billboard_vertices 00705 // Access: Private 00706 // Description: Calculates the vertices for a RM_billboard render. This 00707 // puts a pair of vertices on either side of each 00708 // computed point in curve_segments. 00709 //////////////////////////////////////////////////////////////////// 00710 void RopeNode:: 00711 compute_billboard_vertices(GeomVertexData *vdata, 00712 const LVector3 &camera_vec, 00713 const RopeNode::CurveSegments &curve_segments, 00714 int num_curve_verts, 00715 NurbsCurveResult *result) const { 00716 int expected_num_verts = num_curve_verts * 2; 00717 vdata->set_num_rows(expected_num_verts); 00718 00719 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00720 GeomVertexWriter color(vdata, InternalName::get_color()); 00721 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); 00722 00723 PN_stdfloat thickness = get_thickness(); 00724 PN_stdfloat overall_radius = thickness * 0.5f; 00725 PN_stdfloat radius = overall_radius; 00726 UVMode uv_mode = get_uv_mode(); 00727 PN_stdfloat uv_scale = get_uv_scale(); 00728 bool u_dominant = get_uv_direction(); 00729 bool use_vertex_color = get_use_vertex_color(); 00730 bool use_vertex_thickness = get_use_vertex_thickness(); 00731 00732 PN_stdfloat dist = 0.0f; 00733 CurveSegments::const_iterator si; 00734 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00735 const CurveSegment &segment = (*si); 00736 for (size_t j = 0; j < segment.size(); ++j) { 00737 LVector3 tangent; 00738 compute_tangent(tangent, segment, j, result); 00739 00740 LVector3 norm = cross(tangent, camera_vec); 00741 norm.normalize(); 00742 00743 if (use_vertex_thickness) { 00744 radius = overall_radius * segment[j]._thickness; 00745 } 00746 00747 vertex.add_data3(segment[j]._p + norm * radius); 00748 vertex.add_data3(segment[j]._p - norm * radius); 00749 00750 if (use_vertex_color) { 00751 color.add_data4(segment[j]._c); 00752 color.add_data4(segment[j]._c); 00753 } 00754 00755 PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j); 00756 00757 if (uv_mode != UV_none) { 00758 if (u_dominant) { 00759 texcoord.add_data2(uv_t, 1.0f); 00760 texcoord.add_data2(uv_t, 0.0f); 00761 } else { 00762 texcoord.add_data2(1.0f, uv_t); 00763 texcoord.add_data2(0.0f, uv_t); 00764 } 00765 } 00766 } 00767 } 00768 00769 nassertv(vdata->get_num_rows() == expected_num_verts); 00770 } 00771 00772 //////////////////////////////////////////////////////////////////// 00773 // Function: RopeNode::compute_tube_vertices 00774 // Access: Private 00775 // Description: Calculates the vertices for a RM_tube render. This 00776 // puts a ring of vertices around each computed point in 00777 // curve_segments. 00778 //////////////////////////////////////////////////////////////////// 00779 void RopeNode:: 00780 compute_tube_vertices(GeomVertexData *vdata, 00781 int &num_verts_per_slice, 00782 const RopeNode::CurveSegments &curve_segments, 00783 int num_curve_verts, 00784 NurbsCurveResult *result) const { 00785 int num_slices = get_num_slices(); 00786 num_verts_per_slice = num_slices; 00787 00788 PN_stdfloat thickness = get_thickness(); 00789 PN_stdfloat overall_radius = thickness * 0.5f; 00790 PN_stdfloat radius = overall_radius; 00791 UVMode uv_mode = get_uv_mode(); 00792 PN_stdfloat uv_scale = get_uv_scale(); 00793 bool u_dominant = get_uv_direction(); 00794 NormalMode normal_mode = get_normal_mode(); 00795 bool use_vertex_color = get_use_vertex_color(); 00796 bool use_vertex_thickness = get_use_vertex_thickness(); 00797 00798 // If we are generating UV's, we will need to duplicate the vertices 00799 // along the seam so that the UV's go through the whole range of 00800 // 0..1 instead of reflecting in the last polygon before the seam. 00801 if (uv_mode != UV_none) { 00802 ++num_verts_per_slice; 00803 } 00804 00805 int expected_num_verts = num_curve_verts * num_verts_per_slice; 00806 vdata->set_num_rows(expected_num_verts); 00807 00808 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00809 GeomVertexWriter normal(vdata, InternalName::get_normal()); 00810 GeomVertexWriter color(vdata, InternalName::get_color()); 00811 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); 00812 00813 LVector3 up = get_tube_up(); 00814 00815 PN_stdfloat dist = 0.0f; 00816 CurveSegments::const_iterator si; 00817 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00818 const CurveSegment &segment = (*si); 00819 for (size_t j = 0; j < segment.size(); ++j) { 00820 LVector3 tangent; 00821 compute_tangent(tangent, segment, j, result); 00822 00823 LVector3 norm = cross(tangent, up); 00824 norm.normalize(); 00825 up = cross(norm, tangent); 00826 00827 LMatrix3 rotate = LMatrix3::rotate_mat(360.0f / (PN_stdfloat)num_slices, 00828 tangent); 00829 00830 PN_stdfloat uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j); 00831 00832 for (int s = 0; s < num_verts_per_slice; ++s) { 00833 if (use_vertex_thickness) { 00834 radius = overall_radius * segment[j]._thickness; 00835 } 00836 00837 vertex.add_data3(segment[j]._p + norm * radius); 00838 00839 if (normal_mode == NM_vertex) { 00840 normal.add_data3(norm); 00841 } 00842 00843 if (use_vertex_color) { 00844 color.add_data4(segment[j]._c); 00845 } 00846 00847 norm = norm * rotate; 00848 00849 if (uv_mode != UV_none) { 00850 PN_stdfloat uv_s = (PN_stdfloat)s / (PN_stdfloat)num_slices; 00851 if (u_dominant) { 00852 texcoord.add_data2(uv_t, uv_s); 00853 } else { 00854 texcoord.add_data2(uv_s, uv_t); 00855 } 00856 } 00857 } 00858 } 00859 } 00860 00861 nassertv(vdata->get_num_rows() == expected_num_verts); 00862 } 00863 00864 //////////////////////////////////////////////////////////////////// 00865 // Function: RopeNode::compute_tangent 00866 // Access: Private, Static 00867 // Description: Computes the tangent to the curve at the indicated 00868 // point in the segment. 00869 //////////////////////////////////////////////////////////////////// 00870 void RopeNode:: 00871 compute_tangent(LVector3 &tangent, const RopeNode::CurveSegment &segment, 00872 size_t j, NurbsCurveResult *result) { 00873 // First, try to evaluate the tangent at the curve. This gives 00874 // better results at the ends at the endpoints where the tangent 00875 // does not go to zero. 00876 00877 /* 00878 Actually, on second thought this looks terrible. 00879 00880 if (result->eval_tangent(segment[j]._t, tangent)) { 00881 if (!tangent.almost_equal(LVector3::zero())) { 00882 return; 00883 } 00884 } 00885 */ 00886 00887 // If that failed (or produced a zero tangent), then derive the 00888 // tangent from the neighboring points instead. 00889 if (j == 0) { 00890 tangent = segment[j + 1]._p - segment[j]._p; 00891 } else if (j == segment.size() - 1) { 00892 tangent = segment[j]._p - segment[j - 1]._p; 00893 } else { 00894 tangent = segment[j + 1]._p - segment[j - 1]._p; 00895 } 00896 } 00897 00898 //////////////////////////////////////////////////////////////////// 00899 // Function: RopeNode::compute_uv_t 00900 // Access: Private, Static 00901 // Description: Computes the texture coordinate along the curve for 00902 // the indicated point in the segment. 00903 //////////////////////////////////////////////////////////////////// 00904 PN_stdfloat RopeNode:: 00905 compute_uv_t(PN_stdfloat &dist, const RopeNode::UVMode &uv_mode, 00906 PN_stdfloat uv_scale, const RopeNode::CurveSegment &segment, 00907 size_t j) { 00908 switch (uv_mode) { 00909 case UV_none: 00910 return 0.0f; 00911 00912 case UV_parametric: 00913 return segment[j]._t * uv_scale; 00914 00915 case UV_distance: 00916 if (j != 0) { 00917 LVector3 vec = segment[j]._p - segment[j - 1]._p; 00918 dist += vec.length(); 00919 } 00920 return dist * uv_scale; 00921 00922 case UV_distance2: 00923 if (j != 0) { 00924 LVector3 vec = segment[j]._p - segment[j - 1]._p; 00925 dist += vec.length_squared(); 00926 } 00927 return dist * uv_scale; 00928 } 00929 00930 return 0.0f; 00931 } 00932 00933 //////////////////////////////////////////////////////////////////// 00934 // Function: RopeNode::register_with_read_factory 00935 // Access: Public, Static 00936 // Description: Tells the BamReader how to create objects of type 00937 // RopeNode. 00938 //////////////////////////////////////////////////////////////////// 00939 void RopeNode:: 00940 register_with_read_factory() { 00941 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); 00942 } 00943 00944 //////////////////////////////////////////////////////////////////// 00945 // Function: RopeNode::write_datagram 00946 // Access: Public, Virtual 00947 // Description: Writes the contents of this object to the datagram 00948 // for shipping out to a Bam file. 00949 //////////////////////////////////////////////////////////////////// 00950 void RopeNode:: 00951 write_datagram(BamWriter *manager, Datagram &dg) { 00952 PandaNode::write_datagram(manager, dg); 00953 manager->write_cdata(dg, _cycler); 00954 } 00955 00956 //////////////////////////////////////////////////////////////////// 00957 // Function: RopeNode::make_from_bam 00958 // Access: Protected, Static 00959 // Description: This function is called by the BamReader's factory 00960 // when a new object of type RopeNode is encountered 00961 // in the Bam file. It should create the RopeNode 00962 // and extract its information from the file. 00963 //////////////////////////////////////////////////////////////////// 00964 TypedWritable *RopeNode:: 00965 make_from_bam(const FactoryParams ¶ms) { 00966 RopeNode *node = new RopeNode(""); 00967 DatagramIterator scan; 00968 BamReader *manager; 00969 00970 parse_params(params, scan, manager); 00971 node->fillin(scan, manager); 00972 00973 return node; 00974 } 00975 00976 //////////////////////////////////////////////////////////////////// 00977 // Function: RopeNode::fillin 00978 // Access: Protected 00979 // Description: This internal function is called by make_from_bam to 00980 // read in all of the relevant data from the BamFile for 00981 // the new RopeNode. 00982 //////////////////////////////////////////////////////////////////// 00983 void RopeNode:: 00984 fillin(DatagramIterator &scan, BamReader *manager) { 00985 PandaNode::fillin(scan, manager); 00986 manager->read_cdata(scan, _cycler); 00987 }