gamepad-vgfx.cpp

Go to the documentation of this file.
00001 /*
00002  * gamepad-vgfx.cpp
00003  *
00004  * Copyright (C) 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  * Implementation of a gamepad Type drawer using the vector graphics engine
00032  * (vgfx).
00033  */
00034 
00035 // includes --------------------------------------------------------------------
00036 #include "gamepad-vgfx.h"       // always include our own header first
00037 
00038 #include "color/color.h"
00039 #include "perf/perf.h"
00040 #include "vgfx/request.h"
00041 #include "vgfx/vgfx.h"
00042 
00043 
00044 namespace gamepad {
00045 
00046 
00047 // interface destructors
00048 TypeVgfxRenderer::~TypeVgfxRenderer(void) throw() { }
00049 
00050 
00051 ////////////////////////////////////////////////////////////////////////////////
00052 //
00053 //      static helper methods
00054 //
00055 ////////////////////////////////////////////////////////////////////////////////
00056 
00057 static void
00058 addFloat
00059 (
00060 IN dictionary_t& data,
00061 IN const char * key,
00062 IN float val
00063 )
00064 {
00065         ASSERT(key, "null");
00066 
00067         const int bufsize = 32;
00068         char buffer[bufsize];
00069 
00070         snprintf(buffer, bufsize, "%f", val);
00071 
00072         data[key] = buffer;
00073 }
00074 
00075 
00076 
00077 static bool
00078 getField
00079 (
00080 IN vgfx::ObjectMap * map,
00081 IN const char * id,
00082 IN const char * field,
00083 OUT std::string& value
00084 )
00085 {
00086         ASSERT(map, "null");
00087         ASSERT(id, "null");
00088         ASSERT(field, "null");
00089         value = "";
00090 
00091         vgfx::Primitive * obj = map->findObject(id);
00092         if (!obj)
00093                 return false;   // no such object?
00094 
00095         VecString path;
00096         path.push_back("meta");
00097         path.push_back(field);
00098 
00099         dictionary_t data;
00100 
00101         if (!obj->doesContainerExist(path)) {
00102                 return false;   // no such container (field)
00103         }
00104 
00105         obj->getContainerDictionary(path, data);
00106         const char * val = getValue(data, field);
00107         if (!val)
00108                 return false;   // weird
00109         value = val;
00110         return true;
00111 }
00112 
00113 
00114 
00115 static void
00116 addOrMoveObject
00117 (
00118 IN vgfx::ObjectMap * map,
00119 IN vgfx::Drawer * drawer,
00120 IN const char * baseId,
00121 IN const char * newId,
00122 IN float x,
00123 IN float y,
00124 IN float phi
00125 )
00126 {
00127         ASSERT(map, "null");
00128         ASSERT(drawer, "null");
00129         ASSERT(baseId, "null");
00130         ASSERT(newId, "null");
00131 
00132         // does object already exist?
00133         vgfx::Primitive * root = map->findObject("root");
00134         ASSERT_THROW(root, "gamepad vector graphics contains no 'root'?");
00135 
00136         VecString path;
00137         path.push_back("object");
00138         path.push_back(newId);
00139 
00140         if (!root->doesContainerExist(path)) {
00141                 // object does not exist--add it!
00142                 vgfx::Request req;
00143                 req.addObject("root", newId, baseId, x, y, 0, phi);
00144                 std::string request = req.get();
00145                 //DPRINTF("Request:\n%s", request.c_str());
00146                 std::istringstream iss(request.c_str());
00147                 std::string undo, diagnostic;
00148                 ASSERT_THROW(vgfx::processRequest(map, iss, false, undo, diagnostic),
00149                     "Failed to add new object: " << diagnostic);
00150 
00151                 // recalc bounding rect
00152                 xform_2d_t T;
00153                 T.setIdentity();
00154                 root->recalcBoundingRect("*", drawer, T);
00155                 return;
00156         }
00157 
00158         // just need to move!
00159         dictionary_t data;
00160         addFloat(data, "x", x);
00161         addFloat(data, "y", y);
00162         addFloat(data, "phi", phi);
00163 
00164         root->setContainerDictionary(path, data);
00165 }
00166 
00167 
00168 
00169 static void
00170 removeObject
00171 (
00172 IN vgfx::ObjectMap * map,
00173 IN const char * id
00174 )
00175 {
00176         ASSERT(map, "null");
00177         ASSERT(id, "null");
00178 
00179         // delete!
00180         vgfx::Primitive * root = map->findObject("root");
00181         ASSERT_THROW(root, "gamepad vector graphics contains no 'root'?");
00182 
00183         VecString path;
00184         path.push_back("object");
00185         path.push_back(id);
00186 
00187         if (root->doesContainerExist(path)) {
00188                 root->removeContainer(path);
00189         }
00190 }
00191 
00192 
00193 
00194 ////////////////////////////////////////////////////////////////////////////////
00195 //
00196 //      Renderer -- class that implements the gamepad::TypeVgfxRenderer
00197 //              interface.
00198 //
00199 ////////////////////////////////////////////////////////////////////////////////
00200 
00201 class Renderer : public TypeVgfxRenderer {
00202 public:
00203         // constructor, destructor ---------------------------------------------
00204         Renderer(void) throw();
00205         ~Renderer(void) throw() { }
00206 
00207         // public class methods ------------------------------------------------
00208         void initialize(IN Type * type,
00209                                 IN smart_ptr<vgfx::Drawer>& drawer);
00210 
00211         // gamepad::TypeVgfxRenderer class interface methods -------------------
00212         void setIntensity(IN const char * logical, IN float intensity);
00213         void setPosition(IN const char * logical,
00214                                 IN float x, IN float y);
00215         void draw(IN int xOffest, IN int yOffset,
00216                                 IN int width, IN int height);
00217 
00218 private:
00219         // private typedefs ----------------------------------------------------
00220         struct logical_rec_t {
00221                 // constructor, manipulators
00222                 logical_rec_t(void) throw() { this->clear(); }
00223                 void clear(void) throw() {
00224                                 baseColor.clear();
00225                         }
00226 
00227                 // data fields
00228                 glut_color_t    baseColor;
00229         };
00230 
00231         typedef std::map<std::string, logical_rec_t> logical_map_t;
00232 
00233         // private helper methods ----------------------------------------------
00234         float getScaling(IN int width, IN int height);
00235         logical_rec_t * getLogical(IN const char * logical);
00236         void setJoystick(IN logical_rec_t * plr,
00237                                 IN vgfx::Primitive * obj,
00238                                 IN const char * logical,
00239                                 IN float x, IN float y);
00240         void setDpad(IN logical_rec_t * plr,
00241                                 IN vgfx::Primitive * obj,
00242                                 IN const char * logical,
00243                                 IN float x, IN float y);
00244 
00245         // private member data -------------------------------------------------
00246         smart_ptr<vgfx::ObjectMap>      m_objectMap;
00247         smart_ptr<vgfx::Drawer>         m_drawer;
00248         vgfx::Primitive *               m_vgfxRoot;
00249         vgfx::rect_t                    m_rect; // native bounding rect (cm)
00250         Type *                          m_type;
00251         logical_map_t                   m_logicals;
00252 };
00253 
00254 
00255 
00256 Renderer::Renderer
00257 (
00258 void
00259 )
00260 throw()
00261 {
00262         m_type = NULL;
00263         m_vgfxRoot = NULL;
00264 }
00265 
00266 
00267 
00268 void
00269 Renderer::initialize
00270 (
00271 IN Type * type,
00272 IN smart_ptr<vgfx::Drawer>& drawer
00273 )
00274 {
00275         ASSERT(type, "null");
00276         ASSERT(drawer, "null");
00277         ASSERT(!m_type, "already have a type?");
00278         ASSERT(!m_drawer, "already have a drawer?");
00279         ASSERT(!m_vgfxRoot, "already have a root object?");
00280 
00281         m_type = type;
00282         m_drawer = drawer;
00283 
00284         // try our best to create a vector graphics object...
00285         smart_ptr<nstream::Stream> vgfx = m_type->getVgfx();
00286         if (!vgfx) {
00287                 DPRINTF("Type '%s' has no vector graphics object",
00288                     m_type->getName());
00289                 return;
00290         }
00291 //      DPRINTF("Have vector graphics!");
00292 
00293         m_objectMap = vgfx::ObjectMap::create();
00294         ASSERT(m_objectMap, "null");
00295 
00296         std::istream& stream = vgfx->getStream();
00297         std::string undo, diagnostic;
00298         if (!vgfx::processRequest(m_objectMap, stream, false,
00299             undo, diagnostic)) {
00300                 DPRINTF("ERROR PARSING VGFX FILE:\n%s", diagnostic.c_str());
00301                 return;
00302         }
00303 //      DPRINTF("Finished parsing, object map contains %d objects",
00304 //          (int) m_objectMap->size());
00305 
00306         m_vgfxRoot = m_objectMap->findObject("root");
00307         if (!m_vgfxRoot) {
00308                 DPRINTF("ERROR: vector graphics does not contain a root element");
00309                 return;
00310         }
00311 //      DPRINTF("Successfully loaded vector graphics!");
00312 
00313         // refresh bounding rectangle
00314         xform_2d_t T;
00315         T.setIdentity();
00316         m_vgfxRoot->recalcBoundingRect("*", m_drawer, T);
00317 
00318         // get rect
00319         m_vgfxRoot->getBoundingRect(m_rect);
00320 //      m_rect.dump("Bounding rect");
00321 
00322         // run through and set all colors
00323         VecString names;
00324         m_type->getAllInputs(names);
00325         for (VecString::const_iterator i = names.begin(); i != names.end();
00326              ++i) {
00327                 const char * logical = i->c_str();
00328 
00329                 logical_rec_t lr;
00330                 lr.baseColor.set(1, 1, 1, 1);
00331 
00332                 vgfx::Primitive * obj = m_objectMap->findObject(logical);
00333                 if (!obj) {
00334                         DPRINTF("ERROR: vgfx file is missing logical control: '%s",
00335                             logical);
00336                 } else {
00337                         VecString path;
00338                         path.push_back("meta");
00339                         path.push_back("vgfxColor");
00340 
00341                         if (obj->doesContainerExist(path)) {
00342                                 dictionary_t data;
00343                                 obj->getContainerDictionary(path, data);
00344                                 const char * color =
00345                                     getOptionalValue(data, "vgfxColor", "");
00346                 //              DPRINTF("Color: '%s'", color);
00347 
00348                                 img_color_t ic;
00349                                 getImgColorFromString(color, ic);
00350                                 const float inv = 1.0 / 255.0;
00351                                 lr.baseColor.set(inv * ic.red, inv * ic.green,
00352                                     inv * ic.blue, inv * ic.alpha);
00353                         } else {
00354                                 // DPRINTF("No such path!");
00355                         }
00356                 }
00357 
00358                 // add this logical element's record
00359                 m_logicals[logical] = lr;
00360         }
00361 //      DPRINTF("Logical map contains %d entries", (int) m_logicals.size());
00362 }
00363 
00364 
00365 
00366 ////////////////////////////////////////////////////////////////////////////////
00367 //
00368 //      Renderer -- gamepad::TypeVgfxRenderer class interface methods
00369 //
00370 ////////////////////////////////////////////////////////////////////////////////
00371 
00372 void
00373 Renderer::setIntensity
00374 (
00375 IN const char * logical,
00376 IN float intensity
00377 )
00378 {
00379         ASSERT(logical, "null");
00380         ASSERT(intensity >= 0 && intensity <= 1,
00381             "Bad intensity: %f", intensity);
00382 
00383         // look up the logical element
00384         logical_rec_t * plr = this->getLogical(logical);
00385         if (!plr)
00386                 return;
00387 
00388         // get the scaled color
00389         int iR = (int) (255.0 * intensity * plr->baseColor.red);
00390         int iG = (int) (255.0 * intensity * plr->baseColor.green);
00391         int iB = (int) (255.0 * intensity * plr->baseColor.blue);
00392         int iA = (int) (255.0 * plr->baseColor.alpha);
00393 
00394         const int bufsize = 256;
00395         char buffer[bufsize];
00396         snprintf(buffer, bufsize, "r %d g %d b %d a %d", iR, iG, iB, iA);
00397         //DPRINTF("New color: '%s'", buffer);
00398 
00399         // set up the dictionary
00400         dictionary_t data;
00401         data["vgfxColor"] = buffer;
00402 
00403         // set dictionary on object
00404         VecString path;
00405         path.push_back("meta");
00406         path.push_back("vgfxColor");
00407 
00408         vgfx::Primitive * obj = m_objectMap->findObject(logical);
00409         if (!obj) {
00410                 DPRINTF("No such object in vgfx file: '%s'", logical);
00411                 return;
00412         }
00413 
00414         obj->setContainerDictionary(path, data);
00415 }
00416 
00417 
00418 
00419 void
00420 Renderer::setPosition
00421 (
00422 IN const char * logical,
00423 IN float x,
00424 IN float y
00425 )
00426 {
00427         ASSERT(logical, "null");
00428         ASSERT(x >= 0 && x <= 1, "bad x: %f", x);
00429         ASSERT(y >= 0 && y <= 1, "bad y: %f", y);
00430 
00431         // look up the logical element
00432         logical_rec_t * plr = this->getLogical(logical);
00433         if (!plr)
00434                 return;
00435 
00436         // find the graphics primitive for this object
00437         vgfx::Primitive * obj = m_objectMap->findObject(logical);
00438         if (!obj)
00439                 return;
00440 
00441         eInputType itype = m_type->getInputTypes(logical);
00442         if (eInput_Joystick & itype) {
00443                 this->setJoystick(plr, obj, logical, x, y);
00444         } else if (eInput_Dpad & itype) {
00445                 this->setDpad(plr, obj, logical, x, y);
00446         } else if (eInput_Pot & itype) {
00447 //              this->setPot(obj, plr, logical);
00448         } else {
00449                 // don't do anything for buttons!
00450         }
00451 }
00452 
00453 
00454 
00455 void
00456 Renderer::draw
00457 (
00458 IN int xOffset,
00459 IN int yOffset,
00460 IN int width,
00461 IN int height
00462 )
00463 {
00464         perf::Timer timer("TypeVgfx::draw");
00465         if (!m_vgfxRoot)
00466                 return;
00467         ASSERT(m_drawer, "null");
00468 
00469         float scaling = this->getScaling(width, height);
00470 
00471         xform_2d_t translate;
00472         translate.setIdentity();
00473         translate.setTranslate(xOffset, yOffset);
00474 
00475         xform_2d_t scale;
00476         scale.setTranslate(-m_rect.left, -m_rect.top);
00477         scale.scale(scaling);
00478 
00479         xform_2d_t T;
00480         T.setToProductOf(translate, scale);
00481 
00482         vgfx::rect_t r(xOffset, yOffset, xOffset + width, yOffset + height);
00483 
00484         // use antialiasing!
00485         m_vgfxRoot->draw(m_drawer, r, T);
00486 }
00487 
00488 
00489 
00490 ////////////////////////////////////////////////////////////////////////////////
00491 //
00492 //      Renderer -- private helper methods
00493 //
00494 ////////////////////////////////////////////////////////////////////////////////
00495 
00496 float
00497 Renderer::getScaling
00498 (
00499 IN int width,
00500 IN int height
00501 )
00502 {
00503         ASSERT(width > 0, "bad width: %d", width);
00504         ASSERT(height > 0, "bad height: %d", height);
00505 
00506         float cmX = m_rect.right - m_rect.left;
00507         float cmY = m_rect.bottom - m_rect.top;
00508 
00509         float pixelsPerCmX = width / cmX;
00510         float pixelsPerCmY = height / cmY;
00511 
00512         if (pixelsPerCmX > pixelsPerCmY) {
00513                 return pixelsPerCmY;
00514         } else {
00515                 return pixelsPerCmX;
00516         }
00517 }
00518 
00519 
00520 
00521 Renderer::logical_rec_t *
00522 Renderer::getLogical
00523 (
00524 IN const char * logical
00525 )
00526 {
00527         ASSERT(logical, "null");
00528 
00529         logical_map_t::iterator i = m_logicals.find(logical);
00530         if (m_logicals.end() == i) {
00531                 return NULL;
00532         }
00533         return &i->second;
00534 }
00535 
00536 
00537 
00538 void
00539 Renderer::setJoystick
00540 (
00541 IN logical_rec_t * plr,
00542 IN vgfx::Primitive * obj,
00543 IN const char * logical,
00544 IN float x,
00545 IN float y
00546 )
00547 {
00548         ASSERT(plr, "null");
00549         ASSERT(obj, "null");
00550         ASSERT(logical, "null");
00551         ASSERT(x >= 0 && x <= 1, "bad x: %f", x);
00552         ASSERT(y >= 0 && y <= 1, "bad y: %f", y);
00553 
00554         // DPRINTF("Joystick: x=%5.3f  y=%5.3f", x, y);
00555 
00556         // does the joystick position object exist?
00557         VecString path;
00558         path.push_back("object");
00559         path.push_back("pos");
00560         if (!obj->doesContainerExist(path))
00561                 return;
00562 
00563         x -= 0.5;
00564         y -= 0.5;
00565 
00566         dictionary_t data;
00567         addFloat(data, "x", x);
00568         addFloat(data, "y", y);
00569 
00570         obj->setContainerDictionary(path, data);
00571 }
00572 
00573 
00574 
00575 void
00576 Renderer::setDpad
00577 (
00578 IN logical_rec_t * plr,
00579 IN vgfx::Primitive * obj,
00580 IN const char * logical,
00581 IN float x,
00582 IN float y
00583 )
00584 {
00585         ASSERT(plr, "null");
00586         ASSERT(obj, "null");
00587         ASSERT(logical, "null");
00588         ASSERT(x >= 0 && x <= 1, "bad x: %f", x);
00589         ASSERT(y >= 0 && y <= 1, "bad y: %f", y);
00590 
00591         if (!m_vgfxRoot)
00592                 return;
00593 
00594         // get dpad position
00595         xform_2d_t T;
00596         T.setIdentity();
00597         vgfx::visit_result_t vr;
00598         m_vgfxRoot->getPrimitive(logical, T, vr);
00599         if (!vr.p) {
00600                 DPRINTF("Error: root object does not contain '%s'", logical);
00601                 return;
00602         }
00603         vgfx::point_t p(0, 0);
00604         vgfx::point_t q = vr.T * p;
00605         //q.dump(logical);
00606 
00607         // what is the name of the "up" object?
00608         std::string name;
00609         if (!getField(m_objectMap, logical, "up", name)) {
00610                 DPRINTF("Element '%s' does not contain 'up' meta field", logical);
00611                 return;
00612         }
00613         //DPRINTF("Using name = '%s'", name.c_str());
00614 
00615         std::string xName = logical;
00616         xName += "UpX";
00617         std::string yName = logical;
00618         yName += "UpY";
00619 
00620         // set x-axis position
00621         if (x < 0.25) {
00622                 // left!
00623                 addOrMoveObject(m_objectMap, m_drawer, name.c_str(), xName.c_str(), q.x, q.y, -90);
00624         } else if (x > 0.75) {
00625                 // right!
00626                 addOrMoveObject(m_objectMap, m_drawer, name.c_str(), xName.c_str(), q.x, q.y, +90);
00627         } else {
00628                 // nothing!
00629                 removeObject(m_objectMap, xName.c_str());
00630         }
00631 
00632         // set y-axis position
00633         if (y < 0.25) {
00634                 // up!
00635                 addOrMoveObject(m_objectMap, m_drawer, name.c_str(), yName.c_str(), q.x, q.y, +0);
00636         } else if (y > 0.75) {
00637                 // down!
00638                 addOrMoveObject(m_objectMap, m_drawer, name.c_str(), yName.c_str(), q.x, q.y, +180);
00639         } else {
00640                 // nothing
00641                 removeObject(m_objectMap, yName.c_str());
00642         }
00643 }
00644 
00645 
00646 
00647 ////////////////////////////////////////////////////////////////////////////////
00648 //
00649 //      public API
00650 //
00651 ////////////////////////////////////////////////////////////////////////////////
00652 
00653 smart_ptr<TypeVgfxRenderer>
00654 TypeVgfxRenderer::create
00655 (
00656 IN Type * type,
00657 IN smart_ptr<vgfx::Drawer>& drawer
00658 )
00659 {
00660         ASSERT(type, "null");
00661         ASSERT(drawer, "null");
00662 
00663         smart_ptr<Renderer> local = new Renderer;
00664         ASSERT(local, "out of memory");
00665 
00666         local->initialize(type, drawer);
00667 
00668         return local;
00669 }
00670 
00671 
00672 
00673 };      // gamepad namespace
00674