viewport.cpp

Go to the documentation of this file.
00001 /*
00002  * viewport.cpp
00003  *
00004  * Copyright (C) 2008,2009,2010  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *     * Redistributions of source code must retain the above copyright
00011  *       notice, this list of conditions and the following disclaimer.
00012  *     * Redistributions in binary form must reproduce the above copyright
00013  *       notice, this list of conditions and the following disclaimer in the
00014  *       documentation and/or other materials provided with the distribution.
00015  *     * Neither the name of the <organization> nor the
00016  *       names of its contributors may be used to endorse or promote products
00017  *       derived from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THOMAS A. VAUGHAN ''AS IS'' AND ANY
00020  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00021  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00022  * DISCLAIMED. IN NO EVENT SHALL THOMAS A. VAUGHAN BE LIABLE FOR ANY
00023  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00024  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00025  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00026  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *
00030  *
00031  * Viewport management.  See viewport.h
00032  */
00033 
00034 // includes --------------------------------------------------------------------
00035 #include "viewport.h"                   // always include our own header first!
00036 
00037 #include "geometry/geometry_2d.h"
00038 
00039 #include "glut/glut.h"
00040 #include "glut/glut_2d.h"
00041 
00042 #include "perf/perf.h"
00043 
00044 
00045 namespace view {
00046 
00047 
00048 // interface destructor implementations
00049 Host::~Host(void) throw() { }
00050 Manager::~Manager(void) throw() { }
00051 
00052 
00053 typedef rect2d_t<float> rectf_t;
00054 //typedef rect2d_t<int> recti_t;
00055 
00056 
00057 struct viewport_record_t {
00058         // constructor, manipulators
00059         viewport_record_t(void) throw() { this->clear(); }
00060         void clear(void) throw() {
00061                         id = -1;
00062                         normalizedRect.clear();
00063                         pixelRect.clear();
00064                         renderInfo.clear();
00065                         host = NULL;
00066                 }
00067 
00068         // data fields
00069         int                     id;
00070         rectf_t                 normalizedRect;
00071         recti_t                 pixelRect;
00072         render_info_t           renderInfo;
00073         smart_ptr<Host>         host;
00074 };
00075 
00076 
00077 typedef std::vector<viewport_record_t> view_vec_t;
00078 
00079 
00080 // need a small nonzero epsilon to account for slight floating point errors
00081 static const float s_epsilon                    = 1.0e-8;
00082 
00083 
00084 ////////////////////////////////////////////////////////////////////////////////
00085 //
00086 //      static helper methods
00087 //
00088 ////////////////////////////////////////////////////////////////////////////////
00089 
00090 static void
00091 getPixelRectFromNormalized
00092 (
00093 IN int width,
00094 IN int height,
00095 IN const rectf_t& rf,
00096 OUT recti_t& ri
00097 )
00098 {
00099         ASSERT(width > 0, "bad width: %d", width);
00100         ASSERT(height > 0, "bad height: %d", height);
00101         ASSERT(rf.isValid(), "invalid?");
00102         ASSERT(rf.left >= -s_epsilon && rf.top >= -s_epsilon &&
00103                rf.right <= 1.0 + s_epsilon && rf.bottom <= 1.0 + s_epsilon,
00104             "Malformed input");
00105 
00106         ri.left = (int) (rf.left * width + 0.5);
00107         ri.top = (int) (rf.top * height + 0.5);
00108         ri.right = (int) (rf.right * width + 0.5);
00109         ri.bottom = (int) (rf.bottom * height + 0.5);
00110 
00111         ASSERT(ri.isValid(), "invalid?");
00112         ASSERT(ri.left >= 0 && ri.right <= width &&
00113                ri.top >= 0 && ri.bottom <= height,
00114             "Malformed output");
00115 }
00116 
00117 
00118 
00119 ////////////////////////////////////////////////////////////////////////////////
00120 //
00121 //      Mgr -- class that implements the view::Manager interface
00122 //
00123 ////////////////////////////////////////////////////////////////////////////////
00124 
00125 class Mgr : public Manager {
00126 public:
00127         ~Mgr(void) throw() { }
00128 
00129         // public class methods ------------------------------------------------
00130         void initialize(void);
00131 
00132         // view::Manager class interface methods -------------------------------
00133         int getViewportCount(void) const { return m_viewports.size(); }
00134         int getOwnerOfCoordinate(IN int x, IN int y,
00135                                 IN int width, IN int height,
00136                                 OUT int& ownerX, OUT int& ownerY);
00137         void render(IN int width, IN int height);
00138         bool createViewport(IN int id, IN smart_ptr<Host> host);
00139         bool removeViewport(IN int id);
00140 
00141 private:
00142         // private helper methods-----------------------------------------------
00143         void updateGeometries(void);
00144         view_vec_t::iterator findViewport(IN int id);
00145 
00146         // private member data -------------------------------------------------
00147         view_vec_t              m_viewports;
00148 };
00149 
00150 
00151 
00152 void
00153 Mgr::initialize
00154 (
00155 void
00156 )
00157 {
00158 }
00159 
00160 
00161 
00162 ////////////////////////////////////////////////////////////////////////////////
00163 //
00164 //      Mgr -- view::Manager class interface methods
00165 //
00166 ////////////////////////////////////////////////////////////////////////////////
00167 
00168 void
00169 Mgr::render
00170 (
00171 IN int width,
00172 IN int height
00173 )
00174 {
00175         perf::Timer timer("view::Manager::render");
00176 
00177         ASSERT(width > 0, "bad width: %d", width);
00178         ASSERT(height > 0, "bad height: %d", height);
00179         //DPRINTF("Manager: %d x %d", width, height);
00180 
00181         // save current OpenGL viewport
00182         int oldVP[4];
00183         glGetIntegerv(GL_VIEWPORT, (GLint *) oldVP);
00184 
00185         // first pass: update each viewport's render_info
00186         for (view_vec_t::iterator i = m_viewports.begin();
00187              i != m_viewports.end(); ++i) {
00188 
00189                 viewport_record_t& vr = *i;
00190                 render_info_t& ri = vr.renderInfo;
00191                 recti_t& rc = vr.pixelRect;
00192 
00193                 getPixelRectFromNormalized(width, height, vr.normalizedRect,
00194                     rc);
00195                 //rc.dump("  Drawing/viewport rect");
00196 
00197                 ri.width = rc.right - rc.left + 1;
00198                 ri.height = rc.bottom - rc.top + 1;
00199                 ASSERT(ri.width > 0, "bad viewport width: %d", ri.width);
00200                 ASSERT(ri.height > 0, "bad viewport height: %d", ri.height);
00201         }
00202 
00203         // second pass: all viewports get a shot at 3D drawing
00204         for (view_vec_t::iterator i = m_viewports.begin();
00205              i != m_viewports.end(); ++i) {
00206 
00207                 viewport_record_t& vr = *i;
00208                 render_info_t& ri = vr.renderInfo;
00209                 recti_t& rc = vr.pixelRect;
00210 
00211                 // tell OpenGL to clip to this viewport
00212                 //  remember: 0,0 is LOWER LEFT corner for OpenGL
00213                 int oglY = height - rc.bottom;
00214                 //DPRINTF("    OpenGL Y: %d", oglY);
00215                 glViewport(rc.left, oglY, ri.width, ri.height);
00216 
00217                 // ask the host to render scene then dialogs
00218                 ASSERT(vr.host, "null host");
00219                 vr.host->render3D(vr.id, vr.renderInfo);
00220         }
00221 
00222         // third pass: all viewports get a shot at 2D drawing
00223         glut::push2D(width, height);
00224         for (view_vec_t::iterator i = m_viewports.begin();
00225              i != m_viewports.end(); ++i) {
00226 
00227                 viewport_record_t& vr = *i;
00228                 rectf_t& nr = vr.normalizedRect;
00229                 render_info_t& ri = vr.renderInfo;
00230                 recti_t& rc = vr.pixelRect;
00231 
00232                 // tell OpenGL to clip to this viewport
00233                 int oglY = height - rc.bottom;
00234                 glViewport(rc.left, oglY, ri.width, ri.height);
00235 
00236                 // make the 2D coordinate system relative to this viewport.
00237                 // Note: this is funky!  Because we've specified an OpenGL
00238                 //      viewport (above), we don't need to worry about
00239                 //      translating the origin.  However, glViewport() will
00240                 //      apply scaling automatically!  glViewport() assumes that
00241                 //      we're still using screen coordinates, whereas we'd like
00242                 //      to use window coordinates.  So we add inverse scaling to
00243                 //      account for this, so clients can use window-relative
00244                 //      coordinates.
00245                 glMatrixMode(GL_MODELVIEW);
00246                 glPushMatrix();
00247 
00248                 float xScale = 1.0 / (nr.right - nr.left);
00249                 float yScale = 1.0 / (nr.bottom - nr.top);
00250                 glScalef(xScale, yScale, 1);
00251 
00252                 // call viewport
00253                 ASSERT(vr.host, "null host");
00254                 vr.host->render2D(vr.id, vr.renderInfo);
00255 
00256                 // restore matrix (undo scaling factors)
00257                 glMatrixMode(GL_MODELVIEW);
00258                 glPopMatrix();
00259         }
00260         glut::pop2D();
00261 
00262         // restore original OpenGL viewport
00263         glViewport(oldVP[0], oldVP[1], oldVP[2], oldVP[3]);
00264 }
00265 
00266 
00267 
00268 int
00269 Mgr::getOwnerOfCoordinate
00270 (
00271 IN int x,
00272 IN int y,
00273 IN int width,
00274 IN int height,
00275 OUT int& ownerX,
00276 OUT int& ownerY
00277 )
00278 {
00279         //ASSERT(x >= 0, "bad x: %d", x);
00280         //ASSERT(y >= 0, "bad y: %d", y);
00281         ASSERT(width > 0, "bad width: %d", width);
00282         ASSERT(height > 0, "bad height: %d", height);
00283         //DPRINTF("Looking for owner of (%d, %d)...", x, y);
00284 
00285         // loop through and check each viewport
00286         for (view_vec_t::iterator i = m_viewports.begin();
00287              i != m_viewports.end(); ++i) {
00288 
00289                 viewport_record_t& vr = *i;
00290 
00291                 recti_t rc;             // pixel rect
00292                 getPixelRectFromNormalized(width, height, vr.normalizedRect,
00293                     rc);
00294 
00295                 if (rc.contains(x, y)) {
00296                         //rc.dump("  Contains mouse!");
00297                         //DPRINTF("  id = %d", vr.id);
00298                         ownerX = x - rc.left;
00299                         ownerY = y - rc.top;
00300                         //DPRINTF("    new xy = (%d, %d)", ownerX, ownerY);
00301                         return vr.id;
00302                 }
00303         }
00304 
00305         // no one claimed!
00306         //DPRINTF("  No owner!");
00307         return -1;
00308 }
00309 
00310 
00311 
00312 bool
00313 Mgr::createViewport
00314 (
00315 IN int id,
00316 IN smart_ptr<Host> host
00317 )
00318 {
00319         ASSERT(host, "null");
00320 
00321         //DPRINTF("creating viewport...");
00322 
00323         // valid ID?
00324         if (id < 1) {
00325                 DPRINTF("Invalid id: %d", id);
00326                 return false;
00327         }
00328 
00329         // is this an available ID?
00330         view_vec_t::iterator i = this->findViewport(id);
00331         if (m_viewports.end() != i) {
00332                 DPRINTF("Viewport with id already exists: %d", id);
00333                 return false;
00334         }
00335 
00336         // create a new record
00337         viewport_record_t vr;
00338         vr.id = id;
00339         vr.host = host;
00340 
00341         // add to vector
00342         m_viewports.push_back(vr);
00343 
00344         // update all geometries
00345         this->updateGeometries();
00346 
00347         // that's it!
00348         return true;
00349 }
00350 
00351 
00352 
00353 bool
00354 Mgr::removeViewport
00355 (
00356 IN int id
00357 )
00358 {
00359         view_vec_t::iterator i = this->findViewport(id);
00360         if (m_viewports.end() == i) {
00361                 DPRINTF("Viewport ID not found? %d", id);
00362                 return false;
00363         }
00364 
00365         m_viewports.erase(i);
00366         this->updateGeometries();
00367         return true;
00368 }
00369 
00370 
00371 
00372 ////////////////////////////////////////////////////////////////////////////////
00373 //
00374 //      Mgr -- private helper methods
00375 //
00376 ////////////////////////////////////////////////////////////////////////////////
00377 
00378 void
00379 Mgr::updateGeometries
00380 (
00381 void
00382 )
00383 {
00384         // this is called whenever we need to reset the normalized rects
00385         // Typically this is because a viewport has been added/removed and
00386         // we need to resize viewport sizes etc.
00387 
00388         // TODO: allow for rich customization of viewport geometries
00389         //      (read from config file, etc)
00390         // For now, we just use a rough heuristic
00391         int N = m_viewports.size();
00392         if (!N)
00393                 return;         // nothing to do!
00394 
00395         // how many viewports do we put across the screen?
00396         int width = (int) (sqrt(N) + 0.5);      // heuristic!
00397         ASSERT(width > 0, "bad width: %d", width);
00398 
00399         // how many viewports running down the screen?
00400         int height = N / width;
00401         if (width * height < N) {
00402                 ++height;
00403         }
00404         ASSERT(height > 0, "bad height: %d", height);
00405 
00406         //DPRINTF("Requested %d viewports", N);
00407         //DPRINTF("  Determined %d viewports across screen", width);
00408         //DPRINTF("  Determined %d viewports down screen", height);
00409         //DPRINTF("  %d x %d = %d", width, height, width * height);
00410 
00411         // validate
00412         if (width * height < N ||
00413             width * (height - 1) >= N) {
00414                 ASSERT(false, "bad layout");
00415         }
00416 
00417         // how much space each normalized viewport gets
00418         float dx = 1.0 / width;
00419         float dy = 1.0 / height;
00420 
00421         // run through and give each viewport its normalized coordinates
00422         view_vec_t::iterator iv = m_viewports.begin();
00423         for (int j = 0; j < height; ++j) {
00424                 for (int i = 0; i < width; ++i) {
00425                         if (m_viewports.end() == iv) {
00426                                 break;  // skip
00427                         }
00428 
00429                         viewport_record_t& vr = *iv;
00430                         vr.normalizedRect.left = i * dx;
00431                         vr.normalizedRect.top = j * dy;
00432                         vr.normalizedRect.right = (i + 1) * dx;
00433                         vr.normalizedRect.bottom = (j + 1) * dy;
00434 
00435                         //DPRINTF("Viewport %d:", vr.id);
00436                         //vr.normalizedRect.dump("  normalized");
00437 
00438                         ++iv;
00439                 }
00440         }
00441 }
00442 
00443 
00444 
00445 view_vec_t::iterator
00446 Mgr::findViewport
00447 (
00448 IN int id
00449 )
00450 {
00451         ASSERT(id > 0, "Bad id: %d", id);
00452 
00453         for (view_vec_t::iterator i = m_viewports.begin();
00454              i != m_viewports.end(); ++i) {
00455                 const viewport_record_t& vr = *i;
00456                 if (vr.id == id) {
00457                         return i;
00458                 }
00459         }
00460 
00461         // id not found!
00462         return m_viewports.end();
00463 }
00464 
00465 
00466 
00467 ////////////////////////////////////////////////////////////////////////////////
00468 //
00469 //      public API (Manager factory methods)
00470 //
00471 ////////////////////////////////////////////////////////////////////////////////
00472 
00473 smart_ptr<Manager>
00474 Manager::create
00475 (
00476 void
00477 )
00478 {
00479         smart_ptr<Mgr> local = new Mgr;
00480         ASSERT(local, "out of memory");
00481 
00482         local->initialize();
00483 
00484         return local;
00485 }
00486 
00487 
00488 };      // view namespace
00489