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 "geomLinestrips.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_float32, 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_float32, 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_float32, 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 pvector<LPoint3f> verts; 00319 get_curve()->get_vertices(verts, rel_to); 00320 00321 if (has_matrix()) { 00322 // And then apply the indicated matrix. 00323 const LMatrix4f &mat = get_matrix(); 00324 pvector<LPoint3f>::iterator vi; 00325 for (vi = verts.begin(); vi != verts.end(); ++vi) { 00326 (*vi) = (*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 get_connected_segments(curve_segments, result); 00356 00357 // Now we have stored one or more sequences of vertices down the 00358 // center strips. Go back through and calculate the vertices on 00359 // either side. 00360 PT(GeomVertexData) vdata = new GeomVertexData 00361 ("rope", get_format(false), Geom::UH_stream); 00362 00363 compute_thread_vertices(vdata, curve_segments); 00364 00365 PT(GeomLinestrips) strip = new GeomLinestrips(Geom::UH_stream); 00366 CurveSegments::const_iterator si; 00367 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00368 const CurveSegment &segment = (*si); 00369 00370 strip->add_next_vertices(segment.size()); 00371 strip->close_primitive(); 00372 } 00373 00374 PT(Geom) geom = new Geom(vdata); 00375 geom->add_primitive(strip); 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 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, result); 00415 00416 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); 00417 CurveSegments::const_iterator si; 00418 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00419 const CurveSegment &segment = (*si); 00420 00421 strip->add_next_vertices(segment.size() * 2); 00422 strip->close_primitive(); 00423 } 00424 00425 PT(Geom) geom = new Geom(vdata); 00426 geom->add_primitive(strip); 00427 00428 CPT(RenderState) state = data._state; 00429 if (get_use_vertex_color()) { 00430 state = state->add_attrib(ColorAttrib::make_vertex()); 00431 } 00432 00433 CullableObject *object = 00434 new CullableObject(geom, state, 00435 data.get_net_transform(trav), 00436 data.get_modelview_transform(trav), 00437 trav->get_gsg()); 00438 trav->get_cull_handler()->record_object(object, trav); 00439 } 00440 00441 //////////////////////////////////////////////////////////////////// 00442 // Function: RopeNode::render_billboard 00443 // Access: Private 00444 // Description: Draws the rope in RM_billboard mode. This draws a 00445 // series of triangle strips oriented to be 00446 // perpendicular to the camera plane. 00447 // 00448 // In this mode, thickness is in spatial units, and 00449 // determines the width of the triangle strips. 00450 //////////////////////////////////////////////////////////////////// 00451 void RopeNode:: 00452 render_billboard(CullTraverser *trav, CullTraverserData &data, 00453 NurbsCurveResult *result) const { 00454 const TransformState *net_transform = data.get_net_transform(trav); 00455 const TransformState *camera_transform = trav->get_camera_transform(); 00456 00457 CPT(TransformState) rel_transform = 00458 net_transform->invert_compose(camera_transform); 00459 LVector3f camera_vec = LVector3f::forward() * rel_transform->get_mat(); 00460 00461 CurveSegments curve_segments; 00462 get_connected_segments(curve_segments, result); 00463 00464 // Now we have stored one or more sequences of vertices down the 00465 // center strips. Go back through and calculate the vertices on 00466 // either side. 00467 PT(GeomVertexData) vdata = new GeomVertexData 00468 ("rope", get_format(false), Geom::UH_stream); 00469 00470 compute_billboard_vertices(vdata, camera_vec, 00471 curve_segments, result); 00472 00473 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); 00474 CurveSegments::const_iterator si; 00475 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00476 const CurveSegment &segment = (*si); 00477 00478 strip->add_next_vertices(segment.size() * 2); 00479 strip->close_primitive(); 00480 } 00481 00482 PT(Geom) geom = new Geom(vdata); 00483 geom->add_primitive(strip); 00484 00485 CPT(RenderState) state = data._state; 00486 if (get_use_vertex_color()) { 00487 state = state->add_attrib(ColorAttrib::make_vertex()); 00488 } 00489 00490 CullableObject *object = 00491 new CullableObject(geom, state, 00492 data.get_net_transform(trav), 00493 data.get_modelview_transform(trav), 00494 trav->get_gsg()); 00495 trav->get_cull_handler()->record_object(object, trav); 00496 } 00497 00498 //////////////////////////////////////////////////////////////////// 00499 // Function: RopeNode::render_tube 00500 // Access: Private 00501 // Description: Draws the rope in RM_tube mode. This draws a hollow 00502 // tube centered around the string. 00503 // 00504 // In this mode, thickness is in spatial units, and 00505 // determines the diameter of the tube. 00506 //////////////////////////////////////////////////////////////////// 00507 void RopeNode:: 00508 render_tube(CullTraverser *trav, CullTraverserData &data, 00509 NurbsCurveResult *result) const { 00510 CurveSegments curve_segments; 00511 get_connected_segments(curve_segments, result); 00512 00513 // Now, we build up a table of vertices, in a series of rings 00514 // around the circumference of the tube. 00515 00516 int num_slices = get_num_slices(); 00517 int num_verts_per_slice; 00518 00519 PT(GeomVertexData) vdata = new GeomVertexData 00520 ("rope", get_format(true), Geom::UH_stream); 00521 00522 compute_tube_vertices(vdata, num_verts_per_slice, 00523 curve_segments, result); 00524 00525 PT(GeomTristrips) strip = new GeomTristrips(Geom::UH_stream); 00526 // Finally, go through build up the index array, to tie all the 00527 // triangle strips together. 00528 int vi = 0; 00529 CurveSegments::const_iterator si; 00530 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00531 const CurveSegment &segment = (*si); 00532 00533 for (int s = 0; s < num_slices; ++s) { 00534 int s1 = (s + 1) % num_verts_per_slice; 00535 00536 for (size_t j = 0; j < segment.size(); ++j) { 00537 strip->add_vertex((vi + j) * num_verts_per_slice + s); 00538 strip->add_vertex((vi + j) * num_verts_per_slice + s1); 00539 } 00540 00541 strip->close_primitive(); 00542 } 00543 vi += (int)segment.size(); 00544 } 00545 00546 PT(Geom) geom = new Geom(vdata); 00547 geom->add_primitive(strip); 00548 00549 CPT(RenderState) state = data._state; 00550 if (get_use_vertex_color()) { 00551 state = state->add_attrib(ColorAttrib::make_vertex()); 00552 } 00553 00554 CullableObject *object = 00555 new CullableObject(geom, state, 00556 data.get_net_transform(trav), 00557 data.get_modelview_transform(trav), 00558 trav->get_gsg()); 00559 trav->get_cull_handler()->record_object(object, trav); 00560 } 00561 00562 //////////////////////////////////////////////////////////////////// 00563 // Function: RopeNode::get_connected_segments 00564 // Access: Private 00565 // Description: Evaluates the string of vertices along the curve, and 00566 // also breaks them up into connected segments. 00567 // 00568 // Since the NurbsCurveEvaluator describes the curve as 00569 // a sequence of possibly-connected piecewise continuous 00570 // segments, this means joining together some adjacent 00571 // segments from the NurbsCurveEvaluator into a single 00572 // CurveSegment, if they happen to be connected (as most 00573 // will be). 00574 //////////////////////////////////////////////////////////////////// 00575 void RopeNode:: 00576 get_connected_segments(RopeNode::CurveSegments &curve_segments, 00577 const NurbsCurveResult *result) const { 00578 int num_verts = get_num_subdiv() + 1; 00579 int num_segments = result->get_num_segments(); 00580 bool use_vertex_color = get_use_vertex_color(); 00581 bool use_vertex_thickness = get_use_vertex_thickness(); 00582 00583 CurveSegment *curve_segment = NULL; 00584 LPoint3f last_point; 00585 00586 for (int segment = 0; segment < num_segments; ++segment) { 00587 LPoint3f point; 00588 result->eval_segment_point(segment, 0.0f, point); 00589 00590 if (curve_segment == (CurveSegment *)NULL || 00591 !point.almost_equal(last_point)) { 00592 // If the first point of this segment is different from the last 00593 // point of the previous segment, end the previous segment and 00594 // begin a new one. 00595 curve_segments.push_back(CurveSegment()); 00596 curve_segment = &curve_segments.back(); 00597 00598 CurveVertex vtx; 00599 vtx._p = point; 00600 vtx._t = result->get_segment_t(segment, 0.0f); 00601 if (use_vertex_color) { 00602 result->eval_segment_extended_points(segment, 0.0f, 00603 get_vertex_color_dimension(), 00604 &vtx._c[0], 4); 00605 } 00606 if (use_vertex_thickness) { 00607 vtx._thickness = 00608 result->eval_segment_extended_point(segment, 0.0f, 00609 get_vertex_thickness_dimension()); 00610 } 00611 00612 curve_segment->push_back(vtx); 00613 } 00614 00615 // Store all the remaining points in this segment. 00616 for (int i = 1; i < num_verts; ++i) { 00617 float t = (float)i / (float)(num_verts - 1); 00618 00619 CurveVertex vtx; 00620 result->eval_segment_point(segment, t, vtx._p); 00621 vtx._t = result->get_segment_t(segment, t); 00622 if (use_vertex_color) { 00623 result->eval_segment_extended_points(segment, t, 00624 get_vertex_color_dimension(), 00625 &vtx._c[0], 4); 00626 } 00627 if (use_vertex_thickness) { 00628 vtx._thickness = 00629 result->eval_segment_extended_point(segment, t, 00630 get_vertex_thickness_dimension()); 00631 } 00632 00633 curve_segment->push_back(vtx); 00634 00635 last_point = vtx._p; 00636 } 00637 } 00638 } 00639 00640 //////////////////////////////////////////////////////////////////// 00641 // Function: RopeNode::compute_thread_vertices 00642 // Access: Private 00643 // Description: Calculates the vertices for a RM_thread render. This 00644 // just copies the vertices more-or-less directly into 00645 // the array. 00646 //////////////////////////////////////////////////////////////////// 00647 void RopeNode:: 00648 compute_thread_vertices(GeomVertexData *vdata, 00649 const RopeNode::CurveSegments &curve_segments) const { 00650 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00651 GeomVertexWriter color(vdata, InternalName::get_color()); 00652 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); 00653 00654 UVMode uv_mode = get_uv_mode(); 00655 float uv_scale = get_uv_scale(); 00656 bool u_dominant = get_uv_direction(); 00657 bool use_vertex_color = get_use_vertex_color(); 00658 00659 float dist = 0.0f; 00660 CurveSegments::const_iterator si; 00661 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00662 const CurveSegment &segment = (*si); 00663 for (size_t j = 0; j < segment.size(); ++j) { 00664 vertex.add_data3f(segment[j]._p); 00665 00666 if (use_vertex_color) { 00667 color.add_data4f(segment[j]._c); 00668 } 00669 00670 float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j); 00671 00672 if (uv_mode != UV_none) { 00673 if (u_dominant) { 00674 texcoord.add_data2f(uv_t, 0.0f); 00675 } else { 00676 texcoord.add_data2f(0.0f, uv_t); 00677 } 00678 } 00679 } 00680 } 00681 } 00682 00683 //////////////////////////////////////////////////////////////////// 00684 // Function: RopeNode::compute_billboard_vertices 00685 // Access: Private 00686 // Description: Calculates the vertices for a RM_billboard render. This 00687 // puts a pair of vertices on either side of each 00688 // computed point in curve_segments. 00689 //////////////////////////////////////////////////////////////////// 00690 void RopeNode:: 00691 compute_billboard_vertices(GeomVertexData *vdata, 00692 const LVector3f &camera_vec, 00693 const RopeNode::CurveSegments &curve_segments, 00694 NurbsCurveResult *result) const { 00695 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00696 GeomVertexWriter color(vdata, InternalName::get_color()); 00697 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); 00698 00699 float thickness = get_thickness(); 00700 float overall_radius = thickness * 0.5f; 00701 float radius = overall_radius; 00702 UVMode uv_mode = get_uv_mode(); 00703 float uv_scale = get_uv_scale(); 00704 bool u_dominant = get_uv_direction(); 00705 bool use_vertex_color = get_use_vertex_color(); 00706 bool use_vertex_thickness = get_use_vertex_thickness(); 00707 00708 float dist = 0.0f; 00709 CurveSegments::const_iterator si; 00710 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00711 const CurveSegment &segment = (*si); 00712 for (size_t j = 0; j < segment.size(); ++j) { 00713 LVector3f tangent; 00714 compute_tangent(tangent, segment, j, result); 00715 00716 LVector3f norm = cross(tangent, camera_vec); 00717 norm.normalize(); 00718 00719 if (use_vertex_thickness) { 00720 radius = overall_radius * segment[j]._thickness; 00721 } 00722 00723 vertex.add_data3f(segment[j]._p + norm * radius); 00724 vertex.add_data3f(segment[j]._p - norm * radius); 00725 00726 if (use_vertex_color) { 00727 color.add_data4f(segment[j]._c); 00728 color.add_data4f(segment[j]._c); 00729 } 00730 00731 float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j); 00732 00733 if (uv_mode != UV_none) { 00734 if (u_dominant) { 00735 texcoord.add_data2f(uv_t, 1.0f); 00736 texcoord.add_data2f(uv_t, 0.0f); 00737 } else { 00738 texcoord.add_data2f(1.0f, uv_t); 00739 texcoord.add_data2f(0.0f, uv_t); 00740 } 00741 } 00742 } 00743 } 00744 } 00745 00746 //////////////////////////////////////////////////////////////////// 00747 // Function: RopeNode::compute_tube_vertices 00748 // Access: Private 00749 // Description: Calculates the vertices for a RM_tube render. This 00750 // puts a ring of vertices around each computed point in 00751 // curve_segments. 00752 //////////////////////////////////////////////////////////////////// 00753 void RopeNode:: 00754 compute_tube_vertices(GeomVertexData *vdata, 00755 int &num_verts_per_slice, 00756 const RopeNode::CurveSegments &curve_segments, 00757 NurbsCurveResult *result) const { 00758 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00759 GeomVertexWriter normal(vdata, InternalName::get_normal()); 00760 GeomVertexWriter color(vdata, InternalName::get_color()); 00761 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); 00762 00763 int num_slices = get_num_slices(); 00764 num_verts_per_slice = num_slices; 00765 00766 float thickness = get_thickness(); 00767 float overall_radius = thickness * 0.5f; 00768 float radius = overall_radius; 00769 UVMode uv_mode = get_uv_mode(); 00770 float uv_scale = get_uv_scale(); 00771 bool u_dominant = get_uv_direction(); 00772 NormalMode normal_mode = get_normal_mode(); 00773 bool use_vertex_color = get_use_vertex_color(); 00774 bool use_vertex_thickness = get_use_vertex_thickness(); 00775 00776 // If we are generating UV's, we will need to duplicate the vertices 00777 // along the seam so that the UV's go through the whole range of 00778 // 0..1 instead of reflecting in the last polygon before the seam. 00779 if (uv_mode != UV_none) { 00780 ++num_verts_per_slice; 00781 } 00782 00783 LVector3f up = get_tube_up(); 00784 00785 float dist = 0.0f; 00786 CurveSegments::const_iterator si; 00787 for (si = curve_segments.begin(); si != curve_segments.end(); ++si) { 00788 const CurveSegment &segment = (*si); 00789 for (size_t j = 0; j < segment.size(); ++j) { 00790 LVector3f tangent; 00791 compute_tangent(tangent, segment, j, result); 00792 00793 LVector3f norm = cross(tangent, up); 00794 norm.normalize(); 00795 up = cross(norm, tangent); 00796 00797 LMatrix3f rotate = LMatrix3f::rotate_mat(360.0f / (float)num_slices, 00798 tangent); 00799 00800 float uv_t = compute_uv_t(dist, uv_mode, uv_scale, segment, j); 00801 00802 for (int s = 0; s < num_verts_per_slice; ++s) { 00803 if (use_vertex_thickness) { 00804 radius = overall_radius * segment[j]._thickness; 00805 } 00806 00807 vertex.add_data3f(segment[j]._p + norm * radius); 00808 00809 if (normal_mode == NM_vertex) { 00810 normal.add_data3f(norm); 00811 } 00812 00813 if (use_vertex_color) { 00814 color.add_data4f(segment[j]._c); 00815 } 00816 00817 norm = norm * rotate; 00818 00819 if (uv_mode != UV_none) { 00820 float uv_s = (float)s / (float)num_slices; 00821 if (u_dominant) { 00822 texcoord.add_data2f(uv_t, uv_s); 00823 } else { 00824 texcoord.add_data2f(uv_s, uv_t); 00825 } 00826 } 00827 } 00828 } 00829 } 00830 } 00831 00832 //////////////////////////////////////////////////////////////////// 00833 // Function: RopeNode::compute_tangent 00834 // Access: Private, Static 00835 // Description: Computes the tangent to the curve at the indicated 00836 // point in the segment. 00837 //////////////////////////////////////////////////////////////////// 00838 void RopeNode:: 00839 compute_tangent(LVector3f &tangent, const RopeNode::CurveSegment &segment, 00840 size_t j, NurbsCurveResult *result) { 00841 // First, try to evaluate the tangent at the curve. This gives 00842 // better results at the ends at the endpoints where the tangent 00843 // does not go to zero. 00844 00845 /* 00846 Actually, on second thought this looks terrible. 00847 00848 if (result->eval_tangent(segment[j]._t, tangent)) { 00849 if (!tangent.almost_equal(LVector3f::zero())) { 00850 return; 00851 } 00852 } 00853 */ 00854 00855 // If that failed (or produced a zero tangent), then derive the 00856 // tangent from the neighboring points instead. 00857 if (j == 0) { 00858 tangent = segment[j + 1]._p - segment[j]._p; 00859 } else if (j == segment.size() - 1) { 00860 tangent = segment[j]._p - segment[j - 1]._p; 00861 } else { 00862 tangent = segment[j + 1]._p - segment[j - 1]._p; 00863 } 00864 } 00865 00866 //////////////////////////////////////////////////////////////////// 00867 // Function: RopeNode::compute_uv_t 00868 // Access: Private, Static 00869 // Description: Computes the texture coordinate along the curve for 00870 // the indicated point in the segment. 00871 //////////////////////////////////////////////////////////////////// 00872 float RopeNode:: 00873 compute_uv_t(float &dist, const RopeNode::UVMode &uv_mode, 00874 float uv_scale, const RopeNode::CurveSegment &segment, 00875 size_t j) { 00876 switch (uv_mode) { 00877 case UV_none: 00878 return 0.0f; 00879 00880 case UV_parametric: 00881 return segment[j]._t * uv_scale; 00882 00883 case UV_distance: 00884 if (j != 0) { 00885 LVector3f vec = segment[j]._p - segment[j - 1]._p; 00886 dist += vec.length(); 00887 } 00888 return dist * uv_scale; 00889 00890 case UV_distance2: 00891 if (j != 0) { 00892 LVector3f vec = segment[j]._p - segment[j - 1]._p; 00893 dist += vec.length_squared(); 00894 } 00895 return dist * uv_scale; 00896 } 00897 00898 return 0.0f; 00899 } 00900 00901 //////////////////////////////////////////////////////////////////// 00902 // Function: RopeNode::register_with_read_factory 00903 // Access: Public, Static 00904 // Description: Tells the BamReader how to create objects of type 00905 // RopeNode. 00906 //////////////////////////////////////////////////////////////////// 00907 void RopeNode:: 00908 register_with_read_factory() { 00909 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); 00910 } 00911 00912 //////////////////////////////////////////////////////////////////// 00913 // Function: RopeNode::write_datagram 00914 // Access: Public, Virtual 00915 // Description: Writes the contents of this object to the datagram 00916 // for shipping out to a Bam file. 00917 //////////////////////////////////////////////////////////////////// 00918 void RopeNode:: 00919 write_datagram(BamWriter *manager, Datagram &dg) { 00920 PandaNode::write_datagram(manager, dg); 00921 manager->write_cdata(dg, _cycler); 00922 } 00923 00924 //////////////////////////////////////////////////////////////////// 00925 // Function: RopeNode::make_from_bam 00926 // Access: Protected, Static 00927 // Description: This function is called by the BamReader's factory 00928 // when a new object of type RopeNode is encountered 00929 // in the Bam file. It should create the RopeNode 00930 // and extract its information from the file. 00931 //////////////////////////////////////////////////////////////////// 00932 TypedWritable *RopeNode:: 00933 make_from_bam(const FactoryParams ¶ms) { 00934 RopeNode *node = new RopeNode(""); 00935 DatagramIterator scan; 00936 BamReader *manager; 00937 00938 parse_params(params, scan, manager); 00939 node->fillin(scan, manager); 00940 00941 return node; 00942 } 00943 00944 //////////////////////////////////////////////////////////////////// 00945 // Function: RopeNode::fillin 00946 // Access: Protected 00947 // Description: This internal function is called by make_from_bam to 00948 // read in all of the relevant data from the BamFile for 00949 // the new RopeNode. 00950 //////////////////////////////////////////////////////////////////// 00951 void RopeNode:: 00952 fillin(DatagramIterator &scan, BamReader *manager) { 00953 PandaNode::fillin(scan, manager); 00954 manager->read_cdata(scan, _cycler); 00955 }