Panda3D
|
00001 // Filename: portalClipper.cxx 00002 // Created by: masad (4May04) 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 "portalClipper.h" 00016 #include "cullTraverser.h" 00017 #include "cullTraverserData.h" 00018 #include "transformState.h" 00019 #include "renderState.h" 00020 #include "fogAttrib.h" 00021 #include "cullHandler.h" 00022 #include "dcast.h" 00023 #include "geomNode.h" 00024 #include "config_pgraph.h" 00025 #include "boundingSphere.h" 00026 #include "colorAttrib.h" 00027 #include "renderModeAttrib.h" 00028 #include "cullFaceAttrib.h" 00029 #include "depthOffsetAttrib.h" 00030 #include "geomVertexWriter.h" 00031 #include "geomLinestrips.h" 00032 #include "geomPoints.h" 00033 00034 TypeHandle PortalClipper::_type_handle; 00035 00036 //////////////////////////////////////////////////////////////////// 00037 // Function: PortalClipper::Constructor 00038 // Access: Public 00039 // Description: 00040 //////////////////////////////////////////////////////////////////// 00041 PortalClipper:: 00042 PortalClipper(GeometricBoundingVolume *frustum, SceneSetup *scene_setup): 00043 _reduced_viewport_min(-1,-1), 00044 _reduced_viewport_max(1,1), 00045 _clip_state(0) 00046 { 00047 _previous = new GeomNode("my_frustum"); 00048 00049 _view_frustum = _reduced_frustum = DCAST(BoundingHexahedron, frustum); 00050 00051 00052 _scene_setup = scene_setup; 00053 } 00054 00055 //////////////////////////////////////////////////////////////////// 00056 // Function: PortalClipper::Destructor 00057 // Access: Public 00058 // Description: 00059 //////////////////////////////////////////////////////////////////// 00060 PortalClipper:: 00061 ~PortalClipper() { 00062 } 00063 00064 //////////////////////////////////////////////////////////////////// 00065 // Function: PortalClipper::move_to 00066 // Access: Public 00067 // Description: Moves the pen to the given point without drawing a 00068 // line. When followed by draw_to(), this marks the 00069 // first point of a line segment; when followed by 00070 // move_to() or create(), this creates a single point. 00071 //////////////////////////////////////////////////////////////////// 00072 void PortalClipper:: 00073 move_to(const LVecBase3 &v) { 00074 // We create a new SegmentList with the initial point in it. 00075 SegmentList segs; 00076 segs.push_back(Point(v, _color)); 00077 00078 // And add this list to the list of segments. 00079 _list.push_back(segs); 00080 } 00081 00082 //////////////////////////////////////////////////////////////////// 00083 // Function: PortalClipper::draw_to 00084 // Access: Public 00085 // Description: Draws a line segment from the pen's last position 00086 // (the last call to move_to or draw_to) to the 00087 // indicated point. move_to() and draw_to() only update 00088 // tables; the actual drawing is performed when create() 00089 // is called. 00090 //////////////////////////////////////////////////////////////////// 00091 void PortalClipper:: 00092 draw_to(const LVecBase3 &v) { 00093 if (_list.empty()) { 00094 // Let our first call to draw_to() be an implicit move_to(). 00095 move_to(v); 00096 00097 } else { 00098 // Get the current SegmentList, which was the last one we added to 00099 // the LineList. 00100 SegmentList &segs = _list.back(); 00101 00102 // Add the new point. 00103 segs.push_back(Point(v, _color)); 00104 } 00105 } 00106 00107 //////////////////////////////////////////////////////////////////// 00108 // Function: PortalClipper::draw a portal frustum 00109 // Access: Public 00110 // Description: Given the BoundingHexahedron draw it using lines 00111 // 00112 //////////////////////////////////////////////////////////////////// 00113 void PortalClipper:: 00114 draw_hexahedron(BoundingHexahedron *frustum) { 00115 // walk the view frustum as it should be drawn 00116 move_to(frustum->get_point(0)); 00117 draw_to(frustum->get_point(1)); 00118 draw_to(frustum->get_point(2)); 00119 draw_to(frustum->get_point(3)); 00120 00121 move_to(frustum->get_point(4)); 00122 draw_to(frustum->get_point(0)); 00123 draw_to(frustum->get_point(3)); 00124 draw_to(frustum->get_point(7)); 00125 00126 move_to(frustum->get_point(5)); 00127 draw_to(frustum->get_point(4)); 00128 draw_to(frustum->get_point(7)); 00129 draw_to(frustum->get_point(6)); 00130 00131 move_to(frustum->get_point(1)); 00132 draw_to(frustum->get_point(5)); 00133 draw_to(frustum->get_point(6)); 00134 draw_to(frustum->get_point(2)); 00135 } 00136 //////////////////////////////////////////////////////////////////// 00137 // Function: PortalClipper::draw the current visible portal 00138 // Access: Public 00139 // Description: _portal_node is the current portal, draw it. 00140 // 00141 //////////////////////////////////////////////////////////////////// 00142 void PortalClipper:: 00143 draw_current_portal() 00144 { 00145 move_to(_portal_node->get_vertex(0)); 00146 draw_to(_portal_node->get_vertex(1)); 00147 draw_to(_portal_node->get_vertex(2)); 00148 draw_to(_portal_node->get_vertex(3)); 00149 } 00150 00151 //////////////////////////////////////////////////////////////////// 00152 // Function: PortalClipper::draw the lines 00153 // Access: Public 00154 // Description: Draw all the lines in the buffer 00155 // Cyan portal is the original geometry of the portal 00156 // Yellow portal is the AA minmax & clipped portal 00157 // Blue frustum is the frustum through portal 00158 // White frustum is the camera frustum 00159 //////////////////////////////////////////////////////////////////// 00160 void PortalClipper:: 00161 draw_lines() { 00162 if (!_list.empty()) { 00163 _created_data = NULL; 00164 00165 PT(GeomVertexData) vdata = new GeomVertexData 00166 ("portal", GeomVertexFormat::get_v3cp(), Geom::UH_static); 00167 GeomVertexWriter vertex(vdata, InternalName::get_vertex()); 00168 GeomVertexWriter color(vdata, InternalName::get_color()); 00169 00170 PT(GeomLinestrips) lines = new GeomLinestrips(Geom::UH_static); 00171 PT(GeomPoints) points = new GeomPoints(Geom::UH_static); 00172 00173 int v = 0; 00174 LineList::const_iterator ll; 00175 SegmentList::const_iterator sl; 00176 00177 for (ll = _list.begin(); ll != _list.end(); ll++) { 00178 const SegmentList &segs = (*ll); 00179 00180 if (segs.size() < 2) { 00181 // A segment of length 1 is just a point. 00182 for (sl = segs.begin(); sl != segs.end(); sl++) { 00183 points->add_vertex(v); 00184 vertex.add_data3((*sl)._point); 00185 color.add_data4((*sl)._color); 00186 v++; 00187 } 00188 points->close_primitive(); 00189 00190 } else { 00191 // A segment of length 2 or more is a line segment or 00192 // segments. 00193 for (sl = segs.begin(); sl != segs.end(); sl++) { 00194 lines->add_vertex(v); 00195 vertex.add_data3((*sl)._point); 00196 color.add_data4((*sl)._color); 00197 v++; 00198 } 00199 lines->close_primitive(); 00200 } 00201 } 00202 00203 if (lines->get_num_vertices() != 0) { 00204 PT(Geom) geom = new Geom(vdata); 00205 geom->add_primitive(lines); 00206 _previous->add_geom(geom); 00207 } 00208 if (points->get_num_vertices() != 0) { 00209 PT(Geom) geom = new Geom(vdata); 00210 geom->add_primitive(points); 00211 _previous->add_geom(geom); 00212 } 00213 } 00214 } 00215 //////////////////////////////////////////////////////////////////// 00216 // Function: PortalClipper::prepare the portal 00217 // Access: Public 00218 // Description: Given the portal draw the frustum with line segs 00219 // for now. More functionalities coming up 00220 //////////////////////////////////////////////////////////////////// 00221 bool PortalClipper:: 00222 prepare_portal(const NodePath &node_path) 00223 { 00224 // Get the Portal Node from this node_path 00225 PandaNode *node = node_path.node(); 00226 _portal_node = NULL; 00227 if (node->is_of_type(PortalNode::get_class_type())) { 00228 _portal_node = DCAST(PortalNode, node); 00229 } 00230 00231 // Get the geometry from the portal 00232 portal_cat.spam() << *_portal_node << endl; 00233 00234 // Get the camera transformation matrix 00235 CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_cull_center()); 00236 //CPT(TransformState) ctransform = node_path.get_transform(_scene_setup->get_camera_path()); 00237 LMatrix4 cmat = ctransform->get_mat(); 00238 portal_cat.spam() << cmat << endl; 00239 00240 LVertex temp[4]; 00241 temp[0] = _portal_node->get_vertex(0); 00242 temp[1] = _portal_node->get_vertex(1); 00243 temp[2] = _portal_node->get_vertex(2); 00244 temp[3] = _portal_node->get_vertex(3); 00245 00246 portal_cat.spam() << "before transformation to camera space" << endl; 00247 portal_cat.spam() << temp[0] << endl; 00248 portal_cat.spam() << temp[1] << endl; 00249 portal_cat.spam() << temp[2] << endl; 00250 portal_cat.spam() << temp[3] << endl; 00251 00252 temp[0] = temp[0]*cmat; 00253 temp[1] = temp[1]*cmat; 00254 temp[2] = temp[2]*cmat; 00255 temp[3] = temp[3]*cmat; 00256 00257 LPlane portal_plane(temp[0], temp[1], temp[2]); 00258 if (!is_facing_view(portal_plane)) { 00259 portal_cat.debug() << "portal failed 1st level test (isn't facing the camera)\n"; 00260 return false; 00261 } 00262 00263 portal_cat.spam() << "after transformation to camera space" << endl; 00264 portal_cat.spam() << temp[0] << endl; 00265 portal_cat.spam() << temp[1] << endl; 00266 portal_cat.spam() << temp[2] << endl; 00267 portal_cat.spam() << temp[3] << endl; 00268 00269 // check if the portal intersects with the cameras 0 point (center of projection). In that case the portal will invert itself. 00270 // portals intersecting the near plane or the 0 point are a weird case anyhow, therefore we don't reduce the frustum any further 00271 // and just return true. In effect the portal doesn't reduce visibility but will draw everything in its out cell 00272 const Lens *lens = _scene_setup->get_lens(); 00273 LVector3 forward = LVector3::forward(lens->get_coordinate_system()); 00274 int forward_axis; 00275 if (forward[1]) { 00276 forward_axis = 1; 00277 } 00278 else if (forward[2]) { 00279 forward_axis = 2; 00280 } 00281 else { 00282 forward_axis = 0; 00283 } 00284 if ((temp[0][forward_axis] * forward[forward_axis] <= 0) || 00285 (temp[1][forward_axis] * forward[forward_axis] <= 0) || 00286 (temp[2][forward_axis] * forward[forward_axis] <= 0) || 00287 (temp[3][forward_axis] * forward[forward_axis] <= 0)) { 00288 portal_cat.debug() << "portal intersects with center of projection.." << endl; 00289 return true; 00290 } 00291 00292 // project portal points, so they are in the -1..1 range 00293 LPoint3 projected_coords[4]; 00294 lens->project(temp[0], projected_coords[0]); 00295 lens->project(temp[1], projected_coords[1]); 00296 lens->project(temp[2], projected_coords[2]); 00297 lens->project(temp[3], projected_coords[3]); 00298 00299 portal_cat.spam() << "after projection to 2d" << endl; 00300 portal_cat.spam() << projected_coords[0] << endl; 00301 portal_cat.spam() << projected_coords[1] << endl; 00302 portal_cat.spam() << projected_coords[2] << endl; 00303 portal_cat.spam() << projected_coords[3] << endl; 00304 00305 // calculate axis aligned bounding box of the portal 00306 PN_stdfloat min_x, max_x, min_y, max_y; 00307 min_x = min(min(min(projected_coords[0][0], projected_coords[1][0]), projected_coords[2][0]), projected_coords[3][0]); 00308 max_x = max(max(max(projected_coords[0][0], projected_coords[1][0]), projected_coords[2][0]), projected_coords[3][0]); 00309 min_y = min(min(min(projected_coords[0][1], projected_coords[1][1]), projected_coords[2][1]), projected_coords[3][1]); 00310 max_y = max(max(max(projected_coords[0][1], projected_coords[1][1]), projected_coords[2][1]), projected_coords[3][1]); 00311 00312 portal_cat.spam() << "min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl; 00313 00314 // clip the minima and maxima against the viewport 00315 min_x = max(min_x, _reduced_viewport_min[0]); 00316 min_y = max(min_y, _reduced_viewport_min[1]); 00317 max_x = min(max_x, _reduced_viewport_max[0]); 00318 max_y = min(max_y, _reduced_viewport_max[1]); 00319 00320 portal_cat.spam() << "after clipping: min_x " << min_x << ";max_x " << max_x << ";min_y " << min_y << ";max_y " << max_y << endl; 00321 00322 if ((min_x >= max_x) || (min_y >= max_y)) { 00323 portal_cat.debug() << "portal got clipped away \n"; 00324 return false; 00325 } 00326 00327 // here we know the portal is in view and we have its clipped extents 00328 _reduced_viewport_min.set(min_x, min_y); 00329 _reduced_viewport_max.set(max_x, max_y); 00330 00331 // calculate the near and far points so we can construct a frustum 00332 LPoint3 near_point[4]; 00333 LPoint3 far_point[4]; 00334 lens->extrude(LPoint2(min_x, min_y), near_point[0], far_point[0]); 00335 lens->extrude(LPoint2(max_x, min_y), near_point[1], far_point[1]); 00336 lens->extrude(LPoint2(max_x, max_y), near_point[2], far_point[2]); 00337 lens->extrude(LPoint2(min_x, max_y), near_point[3], far_point[3]); 00338 00339 // With these points, construct the new reduced frustum 00340 _reduced_frustum = new BoundingHexahedron(far_point[0], far_point[1], far_point[2], far_point[3], 00341 near_point[0], near_point[1], near_point[2], near_point[3]); 00342 00343 portal_cat.debug() << *_reduced_frustum << endl; 00344 00345 // do debug rendering, if requested 00346 if (debug_portal_cull) { 00347 // draw the reduced frustum 00348 _color = LColor(0,0,1,1); 00349 draw_hexahedron(DCAST(BoundingHexahedron, _reduced_frustum)); 00350 00351 // lets first add the clipped portal (in yellow) 00352 _color = LColor(1,1,0,1); 00353 move_to((near_point[0]*0.99+far_point[0]*0.01)); // I choose a point in the middle between near and far.. could also be some other z value.. 00354 draw_to((near_point[1]*0.99+far_point[1]*0.01)); 00355 draw_to((near_point[2]*0.99+far_point[2]*0.01)); 00356 draw_to((near_point[3]*0.99+far_point[3]*0.01)); 00357 draw_to((near_point[0]*0.99+far_point[0]*0.01)); 00358 00359 // ok, now lets add the original portal (in cyan) 00360 _color = LColor(0,1,1,1); 00361 move_to(temp[0]); 00362 draw_to(temp[1]); 00363 draw_to(temp[2]); 00364 draw_to(temp[3]); 00365 draw_to(temp[0]); 00366 } 00367 00368 return true; 00369 } 00370