Panda3D
 All Classes Functions Variables Enumerations
portalClipper.cxx
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 
 All Classes Functions Variables Enumerations