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