Panda3D
|
00001 // Filename: portalNode.cxx 00002 // Created by: drose (16Mar02) 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 "portalNode.h" 00016 00017 #include "geomNode.h" 00018 #include "cullTraverserData.h" 00019 #include "cullTraverser.h" 00020 #include "renderState.h" 00021 #include "portalClipper.h" 00022 #include "transformState.h" 00023 #include "colorScaleAttrib.h" 00024 #include "transparencyAttrib.h" 00025 #include "datagram.h" 00026 #include "datagramIterator.h" 00027 #include "bamReader.h" 00028 #include "bamWriter.h" 00029 00030 #include "plane.h" 00031 00032 TypeHandle PortalNode::_type_handle; 00033 00034 00035 //////////////////////////////////////////////////////////////////// 00036 // Function: PortalNode::Constructor 00037 // Access: Public 00038 // Description: Default constructor, just an empty node, no geo 00039 // This is used to read portal from model. You can also 00040 // use this from python to create an empty portal. Then 00041 // you can set the vertices yourself, with addVertex. 00042 //////////////////////////////////////////////////////////////////// 00043 PortalNode:: 00044 PortalNode(const string &name) : 00045 PandaNode(name), 00046 _from_portal_mask(PortalMask::all_on()), 00047 _into_portal_mask(PortalMask::all_on()), 00048 _flags(0) 00049 { 00050 set_cull_callback(); 00051 00052 _visible = false; 00053 _open = true; 00054 _clip_plane = false; 00055 _max_depth = 10; 00056 } 00057 00058 //////////////////////////////////////////////////////////////////// 00059 // Function: PortalNode::Constructor 00060 // Access: Public 00061 // Description: Create a default rectangle as portal. Use this 00062 // to create an arbitrary portal and setup from Python 00063 //////////////////////////////////////////////////////////////////// 00064 PortalNode:: 00065 PortalNode(const string &name, LPoint3 pos, PN_stdfloat scale) : 00066 PandaNode(name), 00067 _from_portal_mask(PortalMask::all_on()), 00068 _into_portal_mask(PortalMask::all_on()), 00069 _flags(0) 00070 { 00071 set_cull_callback(); 00072 00073 add_vertex(LPoint3(pos[0]-1.0*scale, pos[1], pos[2]-1.0*scale)); 00074 add_vertex(LPoint3(pos[0]+1.0*scale, pos[1], pos[2]-1.0*scale)); 00075 add_vertex(LPoint3(pos[0]+1.0*scale, pos[1], pos[2]+1.0*scale)); 00076 add_vertex(LPoint3(pos[0]-1.0*scale, pos[1], pos[2]+1.0*scale)); 00077 00078 _visible = false; 00079 _open = true; 00080 _clip_plane = false; 00081 _max_depth = 10; 00082 } 00083 00084 //////////////////////////////////////////////////////////////////// 00085 // Function: PortalNode::Copy Constructor 00086 // Access: Protected 00087 // Description: 00088 //////////////////////////////////////////////////////////////////// 00089 PortalNode:: 00090 PortalNode(const PortalNode ©) : 00091 PandaNode(copy), 00092 _from_portal_mask(copy._from_portal_mask), 00093 _into_portal_mask(copy._into_portal_mask), 00094 _flags(copy._flags), 00095 _vertices(copy._vertices), 00096 _cell_in(copy._cell_in), 00097 _cell_out(copy._cell_out), 00098 _clip_plane(copy._clip_plane), 00099 _visible(copy._visible), 00100 _open(copy._open), 00101 _max_depth(copy._max_depth) 00102 { 00103 } 00104 00105 //////////////////////////////////////////////////////////////////// 00106 // Function: PortalNode::Destructor 00107 // Access: Public, Virtual 00108 // Description: 00109 //////////////////////////////////////////////////////////////////// 00110 PortalNode:: 00111 ~PortalNode() { 00112 } 00113 00114 //////////////////////////////////////////////////////////////////// 00115 // Function: PortalNode::make_copy 00116 // Access: Public, Virtual 00117 // Description: Returns a newly-allocated Node that is a shallow copy 00118 // of this one. It will be a different Node pointer, 00119 // but its internal data may or may not be shared with 00120 // that of the original Node. 00121 //////////////////////////////////////////////////////////////////// 00122 PandaNode *PortalNode:: 00123 make_copy() const { 00124 return new PortalNode(*this); 00125 } 00126 00127 //////////////////////////////////////////////////////////////////// 00128 // Function: PortalNode::preserve_name 00129 // Access: Public, Virtual 00130 // Description: Returns true if the node's name has extrinsic meaning 00131 // and must be preserved across a flatten operation, 00132 // false otherwise. 00133 //////////////////////////////////////////////////////////////////// 00134 bool PortalNode:: 00135 preserve_name() const { 00136 return true; 00137 } 00138 00139 //////////////////////////////////////////////////////////////////// 00140 // Function: PortalNode::enable_clipping_planes 00141 // Access: Public, Virtual 00142 // Description: initialize the clipping planes and renderstate 00143 //////////////////////////////////////////////////////////////////// 00144 void PortalNode:: 00145 enable_clipping_planes() { 00146 _top_plane_node = new PlaneNode("top"); 00147 NodePath top_plane_np = NodePath(this).attach_new_node(_top_plane_node); 00148 00149 _bottom_plane_node = new PlaneNode("bottom"); 00150 NodePath bottom_plane_np = NodePath(this).attach_new_node(_bottom_plane_node); 00151 00152 _left_plane_node = new PlaneNode("left"); 00153 NodePath left_plane_np = NodePath(this).attach_new_node(_left_plane_node); 00154 00155 _right_plane_node = new PlaneNode("right"); 00156 NodePath right_plane_np = NodePath(this).attach_new_node(_right_plane_node); 00157 00158 CPT(RenderAttrib) plane_attrib = ClipPlaneAttrib::make(); 00159 plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(top_plane_np)); 00160 plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(bottom_plane_np)); 00161 plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(left_plane_np)); 00162 plane_attrib = DCAST(ClipPlaneAttrib, plane_attrib)->add_on_plane(NodePath(right_plane_np)); 00163 00164 _clip_state = RenderState::make(plane_attrib); 00165 } 00166 00167 //////////////////////////////////////////////////////////////////// 00168 // Function: PortalNode::xform 00169 // Access: Public, Virtual 00170 // Description: Transforms the contents of this node by the indicated 00171 // matrix, if it means anything to do so. For most 00172 // kinds of nodes, this does nothing. 00173 //////////////////////////////////////////////////////////////////// 00174 void PortalNode:: 00175 xform(const LMatrix4 &mat) { 00176 nassertv(!mat.is_nan()); 00177 00178 } 00179 00180 //////////////////////////////////////////////////////////////////// 00181 // Function: PortalNode::combine_with 00182 // Access: Public, Virtual 00183 // Description: Collapses this node with the other node, if possible, 00184 // and returns a pointer to the combined node, or NULL 00185 // if the two nodes cannot safely be combined. 00186 // 00187 // The return value may be this, other, or a new node 00188 // altogether. 00189 // 00190 // This function is called from GraphReducer::flatten(), 00191 // and need not deal with children; its job is just to 00192 // decide whether to collapse the two nodes and what the 00193 // collapsed node should look like. 00194 //////////////////////////////////////////////////////////////////// 00195 PandaNode *PortalNode:: 00196 combine_with(PandaNode *other) { 00197 if (is_exact_type(get_class_type()) && 00198 other->is_exact_type(get_class_type())) { 00199 // Two PortalNodes can combine, but only if they have the same 00200 // name, because the name is often meaningful. 00201 PortalNode *cother = DCAST(PortalNode, other); 00202 if (get_name() == cother->get_name()) { 00203 return this; 00204 } 00205 00206 // Two PortalNodes with different names can't combine. 00207 return (PandaNode *)NULL; 00208 } 00209 00210 return PandaNode::combine_with(other); 00211 } 00212 00213 //////////////////////////////////////////////////////////////////// 00214 // Function: PortalNode::cull_callback 00215 // Access: Public, Virtual 00216 // Description: This function will be called during the cull 00217 // traversal to perform reduced frustum 00218 // culling. Basically, once the scenegraph comes across 00219 // a portal node, it calculates a CulltraverserData with 00220 // which cell, this portal leads out to and the new 00221 // frustum. Then it traverses that child 00222 // 00223 // The return value is true if this node should be 00224 // visible, or false if it should be culled. 00225 //////////////////////////////////////////////////////////////////// 00226 bool PortalNode:: 00227 cull_callback(CullTraverser *trav, CullTraverserData &data) { 00228 Thread *current_thread = trav->get_current_thread(); 00229 00230 PortalClipper *portal_viewer = trav->get_portal_clipper(); 00231 set_visible(false); 00232 if (is_open() && !_cell_out.is_empty() && portal_viewer && data._portal_depth <= _max_depth) { 00233 portal_cat.debug() << "checking portal node " << *this << endl; 00234 portal_cat.debug() << "portal_depth is " << data._portal_depth << endl; 00235 PT(GeometricBoundingVolume) vf = trav->get_view_frustum(); 00236 PT(BoundingVolume) reduced_frustum; 00237 00238 // remember old viewport and frustum, so we can restore them for the siblings. (it gets changed by the prepare_portal call) 00239 LPoint2 old_reduced_viewport_min, old_reduced_viewport_max; 00240 portal_viewer->get_reduced_viewport(old_reduced_viewport_min, old_reduced_viewport_max); 00241 PT(BoundingHexahedron) old_bh = portal_viewer->get_reduced_frustum(); 00242 00243 if (portal_viewer->prepare_portal(data._node_path.get_node_path())) { 00244 if ((reduced_frustum = portal_viewer->get_reduced_frustum())) { 00245 // remember current clip state, we might change it 00246 CPT(RenderState) old_clip_state = portal_viewer->get_clip_state(); 00247 00248 set_visible(true); 00249 // The frustum is in camera space 00250 vf = DCAST(GeometricBoundingVolume, reduced_frustum); 00251 00252 // create a copy of this reduced frustum, we'll transform it from camera space to the cell_out space 00253 PT(BoundingHexahedron) new_bh = DCAST(BoundingHexahedron, vf->make_copy()); 00254 00255 // Get the net trasform of the _cell_out as seen from the camera. 00256 CPT(TransformState) cell_transform = _cell_out.get_net_transform(); 00257 CPT(TransformState) frustum_transform = cell_transform ->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform()); 00258 00259 // transform to _cell_out space 00260 new_bh->xform(frustum_transform->get_mat()); 00261 00262 CPT(RenderState) next_state = data._state; 00263 00264 // set clipping planes, if desired.. 00265 if (_clip_plane) { 00266 // create a copy of this reduced frustum, we'll transform it from camera space to this portal node's space (because the clip planes are attached to this node) 00267 PT(BoundingHexahedron) temp_bh = DCAST(BoundingHexahedron, vf->make_copy()); 00268 CPT(TransformState) temp_frustum_transform = data._node_path.get_node_path().get_net_transform()->invert_compose(portal_viewer->_scene_setup->get_cull_center().get_net_transform()); 00269 00270 portal_cat.spam() << "clipping plane frustum transform " << *temp_frustum_transform << endl; 00271 portal_cat.spam() << "frustum before transform " << *temp_bh << endl; 00272 // transform to portalNode space 00273 temp_bh->xform(temp_frustum_transform->get_mat()); 00274 00275 portal_cat.spam() << "frustum after transform " << *temp_bh << endl; 00276 00277 _left_plane_node->set_plane(-temp_bh->get_plane(4)); // left plane of bh 00278 _right_plane_node->set_plane(-temp_bh->get_plane(2));// right plane of bh 00279 _top_plane_node->set_plane(-temp_bh->get_plane(3)); // top plane of bh 00280 _bottom_plane_node->set_plane(-temp_bh->get_plane(1));// bottom plane of bh 00281 00282 portal_cat.spam() << "left plane " << *_left_plane_node << endl; 00283 portal_cat.spam() << "right plane " << *_right_plane_node << endl; 00284 portal_cat.spam() << "top plane " << *_top_plane_node << endl; 00285 portal_cat.spam() << "bottom plane " << *_bottom_plane_node << endl; 00286 00287 // remember the clip state we just generated 00288 portal_viewer->set_clip_state(_clip_state); 00289 00290 if (old_clip_state) { 00291 portal_cat.spam() << "parent clip state " << *old_clip_state << endl; 00292 } else { 00293 portal_cat.spam() << "parent clip state None" << endl; 00294 } 00295 portal_cat.spam() << "own clip state " << *_clip_state << endl; 00296 portal_cat.spam() << "next state " << *next_state << endl; 00297 00298 // undo parent clip state and compose our new clip state ito the new state 00299 if (old_clip_state != NULL) { 00300 next_state = old_clip_state->invert_compose(next_state); 00301 portal_cat.spam() << "next state after removing parent state " << *next_state << endl; 00302 } 00303 next_state = next_state->compose(_clip_state); 00304 portal_cat.spam() << "next state after composition " << *next_state << endl; 00305 } 00306 00307 CullTraverserData next_data(_cell_out, 00308 cell_transform, 00309 next_state, new_bh, 00310 current_thread); 00311 next_data._portal_depth = data._portal_depth + 1; 00312 00313 portal_viewer->set_reduced_frustum(new_bh); 00314 portal_cat.spam() << "cull_callback: before traversing " << _cell_out.get_name() << endl; 00315 trav->traverse_below(next_data); 00316 portal_cat.spam() << "cull_callback: after traversing " << _cell_out.get_name() << endl; 00317 00318 // restore clip state 00319 portal_viewer->set_clip_state(old_clip_state); 00320 } 00321 } 00322 // reset portal viewer frustum for the siblings; 00323 portal_viewer->set_reduced_frustum(old_bh); 00324 // reset portal viewer viewport for the siblings; 00325 portal_viewer->set_reduced_viewport(old_reduced_viewport_min, old_reduced_viewport_max); 00326 } 00327 // Now carry on to render our child nodes. 00328 return true; 00329 } 00330 00331 //////////////////////////////////////////////////////////////////// 00332 // Function: PortalNode::is_renderable 00333 // Access: Public, Virtual 00334 // Description: Returns true if there is some value to visiting this 00335 // particular node during the cull traversal for any 00336 // camera, false otherwise. This will be used to 00337 // optimize the result of get_net_draw_show_mask(), so 00338 // that any subtrees that contain only nodes for which 00339 // is_renderable() is false need not be visited. 00340 //////////////////////////////////////////////////////////////////// 00341 bool PortalNode:: 00342 is_renderable() const { 00343 return true; 00344 } 00345 00346 00347 //////////////////////////////////////////////////////////////////// 00348 // Function: PortalNode::output 00349 // Access: Public, Virtual 00350 // Description: Writes a brief description of the node to the 00351 // indicated output stream. This is invoked by the << 00352 // operator. It may be overridden in derived classes to 00353 // include some information relevant to the class. 00354 //////////////////////////////////////////////////////////////////// 00355 void PortalNode:: 00356 output(ostream &out) const { 00357 PandaNode::output(out); 00358 } 00359 00360 /* 00361 //////////////////////////////////////////////////////////////////// 00362 // Function: PortalNode::draw 00363 // Access: Public 00364 // Description: Draws the vertices of this portal rectangle to the 00365 // screen with a line 00366 00367 //////////////////////////////////////////////////////////////////// 00368 void PortalNode:: 00369 draw() const { 00370 move_to(get_vertex(0)); 00371 draw_to(get_vertex(1)); 00372 draw_to(get_vertex(2)); 00373 draw_to(get_vertex(3)); 00374 } 00375 */ 00376 00377 //////////////////////////////////////////////////////////////////// 00378 // Function: PortalNode::compute_internal_bounds 00379 // Access: Protected, Virtual 00380 // Description: Called when needed to recompute the node's 00381 // _internal_bound object. Nodes that contain anything 00382 // of substance should redefine this to do the right 00383 // thing. 00384 //////////////////////////////////////////////////////////////////// 00385 void PortalNode:: 00386 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, 00387 int &internal_vertices, 00388 int pipeline_stage, 00389 Thread *current_thread) const { 00390 // First, get ourselves a fresh, empty bounding volume. 00391 PT(BoundingVolume) bound = new BoundingSphere; 00392 GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound); 00393 00394 // Now actually compute the bounding volume by putting it around all 00395 // of our vertices. 00396 00397 const LPoint3 *vertices_begin = &_vertices[0]; 00398 const LPoint3 *vertices_end = vertices_begin + _vertices.size(); 00399 00400 // Now actually compute the bounding volume by putting it around all 00401 gbv->around(vertices_begin, vertices_end); 00402 00403 internal_bounds = bound; 00404 internal_vertices = 0; 00405 } 00406 00407 //////////////////////////////////////////////////////////////////// 00408 // Function: PortalNode::get_last_pos_state 00409 // Access: Protected 00410 // Description: Returns a RenderState for rendering the ghosted 00411 // portal rectangle that represents the previous frame's 00412 // position, for those collision nodes that indicate a 00413 // velocity. 00414 //////////////////////////////////////////////////////////////////// 00415 CPT(RenderState) PortalNode:: 00416 get_last_pos_state() { 00417 // Once someone asks for this pointer, we hold its reference count 00418 // and never free it. 00419 static CPT(RenderState) state = (const RenderState *)NULL; 00420 if (state == (const RenderState *)NULL) { 00421 state = RenderState::make 00422 (ColorScaleAttrib::make(LVecBase4(1.0f, 1.0f, 1.0f, 0.5f)), 00423 TransparencyAttrib::make(TransparencyAttrib::M_alpha)); 00424 } 00425 00426 return state; 00427 } 00428 00429 00430 //////////////////////////////////////////////////////////////////// 00431 // Function: PortalNode::register_with_read_factory 00432 // Access: Public, Static 00433 // Description: Tells the BamReader how to create objects of type 00434 // PortalNode. 00435 //////////////////////////////////////////////////////////////////// 00436 void PortalNode:: 00437 register_with_read_factory() { 00438 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); 00439 } 00440 00441 //////////////////////////////////////////////////////////////////// 00442 // Function: PortalNode::write_datagram 00443 // Access: Public, Virtual 00444 // Description: Writes the contents of this object to the datagram 00445 // for shipping out to a Bam file. 00446 //////////////////////////////////////////////////////////////////// 00447 void PortalNode:: 00448 write_datagram(BamWriter *manager, Datagram &dg) { 00449 PandaNode::write_datagram(manager, dg); 00450 00451 dg.add_uint16(_vertices.size()); 00452 for (Vertices::const_iterator vi = _vertices.begin(); 00453 vi != _vertices.end(); 00454 ++vi) { 00455 (*vi).write_datagram(dg); 00456 } 00457 } 00458 00459 //////////////////////////////////////////////////////////////////// 00460 // Function: PortalNode::complete_pointers 00461 // Access: Public, Virtual 00462 // Description: Receives an array of pointers, one for each time 00463 // manager->read_pointer() was called in fillin(). 00464 // Returns the number of pointers processed. 00465 //////////////////////////////////////////////////////////////////// 00466 int PortalNode:: 00467 complete_pointers(TypedWritable **p_list, BamReader *manager) { 00468 int pi = PandaNode::complete_pointers(p_list, manager); 00469 00470 return pi; 00471 } 00472 00473 //////////////////////////////////////////////////////////////////// 00474 // Function: PortalNode::make_from_bam 00475 // Access: Protected, Static 00476 // Description: This function is called by the BamReader's factory 00477 // when a new object of type PortalNode is encountered 00478 // in the Bam file. It should create the PortalNode 00479 // and extract its information from the file. 00480 //////////////////////////////////////////////////////////////////// 00481 TypedWritable *PortalNode:: 00482 make_from_bam(const FactoryParams ¶ms) { 00483 PortalNode *node = new PortalNode(""); 00484 DatagramIterator scan; 00485 BamReader *manager; 00486 00487 parse_params(params, scan, manager); 00488 node->fillin(scan, manager); 00489 00490 return node; 00491 } 00492 00493 //////////////////////////////////////////////////////////////////// 00494 // Function: PortalNode::fillin 00495 // Access: Protected 00496 // Description: This internal function is called by make_from_bam to 00497 // read in all of the relevant data from the BamFile for 00498 // the new PortalNode. 00499 //////////////////////////////////////////////////////////////////// 00500 void PortalNode:: 00501 fillin(DatagramIterator &scan, BamReader *manager) { 00502 PandaNode::fillin(scan, manager); 00503 00504 int num_vertices = scan.get_uint16(); 00505 _vertices.reserve(num_vertices); 00506 for (int i = 0; i < num_vertices; i++) { 00507 LPoint3 vertex; 00508 vertex.read_datagram(scan); 00509 _vertices.push_back(vertex); 00510 } 00511 00512 /* 00513 _from_portal_mask.set_word(scan.get_uint32()); 00514 _into_portal_mask.set_word(scan.get_uint32()); 00515 _flags = scan.get_uint8(); 00516 */ 00517 }