UWSimUtils.cpp
Go to the documentation of this file.
00001 /* 
00002  * Copyright (c) 2013 University of Jaume-I.
00003  * All rights reserved. This program and the accompanying materials
00004  * are made available under the terms of the GNU Public License v3.0
00005  * which accompanies this distribution, and is available at
00006  * http://www.gnu.org/licenses/gpl.html
00007  * 
00008  * Contributors:
00009  *     Mario Prats
00010  *     Javier Perez
00011  */
00012 
00013 #include <ros/ros.h>
00014 #include <uwsim/SimulatorConfig.h>
00015 #include <uwsim/UWSimUtils.h>
00016 #include <osg/Material>
00017 #include <osg/ShapeDrawable>
00018 #include <osg/Shape>
00019 #include <osg/Geode>
00020 #include <osg/Switch>
00021 #include <osg/MatrixTransform>
00022 #include <osgDB/ReadFile>
00023 
00024 #if OSG_VERSION_MAJOR>=3
00025 #include <osgDB/Options>
00026 #endif
00027 
00028 
00029 // Default constructor - initialize searchForName to "" and 
00030 // set the traversal mode to TRAVERSE_ALL_CHILDREN
00031 findNodeVisitor::findNodeVisitor() :
00032     osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), searchForName()
00033 {
00034 }
00035 
00036 // Constructor that accepts string argument
00037 // Initializes searchForName to user string
00038 // set the traversal mode to TRAVERSE_ALL_CHILDREN
00039 findNodeVisitor::findNodeVisitor(const std::string &searchName) :
00040     osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), searchForName(searchName)
00041 {
00042 }
00043 
00044 //The 'apply' method for 'node' type instances.
00045 //Compare the 'searchForName' data member against the node's name.
00046 //If the strings match, add this node to our list
00047 void findNodeVisitor::apply(osg::Node &searchNode)
00048 {
00049   //std::cerr << "Compare " << searchForName << " to "  << searchNode.getName() << std::endl;
00050   if (searchNode.getName() == searchForName)
00051   {
00052     foundNodeList.push_back(&searchNode);
00053   }
00054   traverse(searchNode);
00055 }
00056 
00057 // Set the searchForName to user-defined string
00058 void findNodeVisitor::setNameToFind(const std::string &searchName)
00059 {
00060   searchForName = searchName;
00061   foundNodeList.clear();
00062 }
00063 
00064 osg::Node* findNodeVisitor::getFirst()
00065 {
00066   if (foundNodeList.size() == 0)
00067     return NULL;
00068   else
00069     return foundNodeList[0];
00070 }
00071 
00072 findRoutedNode::findRoutedNode() :
00073     nodeVisitor()
00074 {
00075 }
00076 
00077 findRoutedNode::findRoutedNode(const std::string &searchName) :
00078     nodeVisitor(), searchRoute(searchName)
00079 {
00080 }
00081 
00082 void findRoutedNode::setNameToFind(const std::string &searchName)
00083 {
00084   searchRoute = searchName;
00085   rootList.clear();
00086 }
00087 
00088 void findRoutedNode::find(osg::ref_ptr<osg::Node> searchNode)
00089 {
00090   unsigned int pos = 0;
00091   rootList.clear();
00092   rootList.push_back(searchNode);
00093   nodeListType auxList, auxList2;
00094 
00095   while ((pos = searchRoute.find("/")) < searchRoute.size())
00096   {
00097     for (unsigned int i = 0; i < rootList.size(); i++)
00098     {
00099       nodeVisitor.setNameToFind(searchRoute.substr(0, pos));
00100       rootList[i]->accept(nodeVisitor);
00101       auxList2 = nodeVisitor.getNodeList();
00102       auxList.insert(auxList.end(), auxList2.begin(), auxList2.end());
00103     }
00104     searchRoute.erase(0, pos + 1);
00105     rootList = auxList;
00106     auxList.clear();
00107   }
00108   for (unsigned int i = 0; i < rootList.size(); i++)
00109   {
00110     nodeVisitor.setNameToFind(searchRoute);
00111     rootList[i]->accept(nodeVisitor);
00112     auxList2 = nodeVisitor.getNodeList();
00113     auxList.insert(auxList.end(), auxList2.begin(), auxList2.end());
00114   }
00115   rootList = auxList;
00116 }
00117 
00118 osg::Node* findRoutedNode::getFirst()
00119 {
00120   if (rootList.size() == 0)
00121     return NULL;
00122   else
00123     return rootList[0];
00124 }
00125 
00126 osg::Node * findRN(std::string target, osg::Group * root)
00127 {
00128   findRoutedNode findRN(target);
00129   findRN.find(root);
00130   return findRN.getFirst();
00131 }
00132 
00133 //Default mask is for AR objects (not shown on Virtual Cameras)
00134 osg::Node* UWSimGeometry::createSwitchableFrame(double radius, double length, unsigned int mask)
00135 {
00136   osg::Switch *axis = new osg::Switch();
00137   axis->setNewChildDefaultValue(false);
00138   axis->setName("switch_frames");
00139   axis->addChild(UWSimGeometry::createFrame());
00140   axis->setNodeMask(mask);
00141   return axis;
00142 }
00143 
00144 osg::Node* UWSimGeometry::createFrame(double radius, double length)
00145 {
00146   osg::Matrix linkBaseMatrix;
00147   linkBaseMatrix.makeIdentity();
00148   osg::MatrixTransform *linkBaseTransform = new osg::MatrixTransform(linkBaseMatrix);
00149 
00150   //create XBase to rotate
00151   osg::Matrix XBase;
00152   XBase.makeIdentity();
00153   XBase.preMultRotate(osg::Quat(M_PI_2, osg::Vec3d(0, 1, 0)));
00154   XBase.preMultTranslate(osg::Vec3d(0, 0, length / 2));
00155   osg::MatrixTransform *XBaseTransform = new osg::MatrixTransform(XBase);
00156   linkBaseTransform->addChild(XBaseTransform);
00157 
00158   //create X cylinder, set color, and add to XBase
00159   osg::Node * Xcylinder = UWSimGeometry::createOSGCylinder(radius, length);
00160   osg::StateSet * Xstateset = new osg::StateSet();
00161   osg::Material * Xmaterial = new osg::Material();
00162   Xmaterial->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(1, 0, 0, 0));
00163   Xstateset->setAttribute(Xmaterial);
00164   Xcylinder->setStateSet(Xstateset);
00165   XBaseTransform->addChild(Xcylinder);
00166 
00167   //create YBase to rotate
00168   osg::Matrix YBase;
00169   YBase.preMultRotate(osg::Quat(M_PI_2, osg::Vec3d(1, 0, 0)));
00170   YBase.preMultTranslate(osg::Vec3d(0, 0, -length / 2));
00171   osg::MatrixTransform *YBaseTransform = new osg::MatrixTransform(YBase);
00172   linkBaseTransform->addChild(YBaseTransform);
00173 
00174   //create Y cylinder, set color, and add to YBase
00175   osg::Node *Ycylinder = UWSimGeometry::createOSGCylinder(radius, length);
00176   osg::StateSet * Ystateset = new osg::StateSet();
00177   osg::Material * Ymaterial = new osg::Material();
00178   Ymaterial->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 1, 0, 0));
00179   Ystateset->setAttribute(Ymaterial);
00180   Ycylinder->setStateSet(Ystateset);
00181   YBaseTransform->addChild(Ycylinder);
00182 
00183   //create ZBase to rotate
00184   osg::Matrix ZBase;
00185   ZBase.makeIdentity();
00186   ZBase.preMultRotate(osg::Quat(M_PI_2, osg::Vec3d(0, 0, 1)));
00187   ZBase.preMultTranslate(osg::Vec3d(0, 0, length / 2));
00188   osg::MatrixTransform *ZBaseTransform = new osg::MatrixTransform(ZBase);
00189   linkBaseTransform->addChild(ZBaseTransform);
00190 
00191   //create Z cylinder, set color, and add to ZBase
00192   osg::Node * Zcylinder = UWSimGeometry::createOSGCylinder(radius, length);
00193   osg::StateSet * Zstateset = new osg::StateSet();
00194   osg::Material * Zmaterial = new osg::Material();
00195   Zmaterial->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4(0, 0, 1, 0));
00196   Zstateset->setAttribute(Zmaterial);
00197   Zcylinder->setStateSet(Zstateset);
00198   ZBaseTransform->addChild(Zcylinder);
00199 
00200   return linkBaseTransform;
00201 }
00202 
00203 osg::Node * UWSimGeometry::createOSGBox(osg::Vec3 size)
00204 {
00205   osg::Box *box = new osg::Box();
00206 
00207   box->setHalfLengths(size / 2);
00208 
00209   osg::ShapeDrawable *shape = new osg::ShapeDrawable(box);
00210   osg::Geode *geode = new osg::Geode();
00211   geode->addDrawable(shape);
00212 
00213   osg::Node* node = new osg::Group();
00214   node->asGroup()->addChild(geode);
00215 
00216   return node;
00217 }
00218 
00219 osg::Node* UWSimGeometry::createOSGCylinder(double radius, double height)
00220 {
00221   osg::Cylinder *cylinder = new osg::Cylinder();
00222 
00223   cylinder->setRadius(radius);
00224   cylinder->setHeight(height);
00225 
00226   osg::ShapeDrawable *shape = new osg::ShapeDrawable(cylinder);
00227   osg::Geode *geode = new osg::Geode();
00228   geode->addDrawable(shape);
00229 
00230   osg::Node *node = new osg::Group();
00231   node->asGroup()->addChild(geode);
00232 
00233   return node;
00234 }
00235 
00236 osg::Node * UWSimGeometry::createOSGSphere(double radius)
00237 {
00238   osg::Sphere *sphere = new osg::Sphere();
00239 
00240   sphere->setRadius(radius);
00241 
00242   osg::ShapeDrawable *shape = new osg::ShapeDrawable(sphere);
00243   osg::Geode *geode = new osg::Geode();
00244   geode->addDrawable(shape);
00245 
00246   osg::Node *node = new osg::Group();
00247   node->asGroup()->addChild(geode);
00248 
00249   return node;
00250 }
00251 
00252 osg::Node * UWSimGeometry::createLabel(std::string textToDraw,double charSize, int bb, osg::Vec4 color )
00253 {
00254   //Create text
00255   osg::ref_ptr<osgText::Text> text = new osgText::Text;
00256   text->setFont( "fonts/arial.ttf" );
00257   text->setText(textToDraw);
00258   text->setAxisAlignment( osgText::TextBase::SCREEN );
00259   text->setDataVariance( osg::Object::DYNAMIC );
00260   text->setColor(color);
00261   text->setCharacterSize(charSize);
00262   if(bb)
00263   {
00264     text->setBoundingBoxColor(color);
00265     text->setDrawMode(osgText::Text::TEXT | osgText::Text::ALIGNMENT | osgText::Text::BOUNDINGBOX);
00266   }
00267   else
00268     text->setDrawMode(osgText::Text::TEXT | osgText::Text::ALIGNMENT);
00269 
00270   //set visual properties
00271   osg::ref_ptr<osg::Geode> geode = new osg::Geode;
00272   geode->addDrawable( text.get() );
00273   geode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF); //Draw it over geometry
00274   geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF );  //Ignore shadows
00275 
00276   geode->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::ON); //Unset shader
00277   return geode.release();
00278 }
00279 
00280 osg::Node * UWSimGeometry::retrieveResource(std::string name)
00281 {
00282   //Load file in memory
00283   resource_retriever::Retriever r;
00284   resource_retriever::MemoryResource resource;
00285 
00286   try
00287   {
00288     resource = r.get(name);
00289   }
00290   catch (resource_retriever::Exception& e)
00291   {
00292     return NULL;
00293   }
00294 
00295   //Create stream with memory resource
00296   std::stringstream buffer;
00297   buffer.write((char *)resource.data.get(), resource.size);
00298 
00299   //Get file extension and create options
00300   std::string file_ext = osgDB::getFileExtension(name);
00301 #if OSG_VERSION_MAJOR>=3
00302   osg::ref_ptr<osgDB::Options> local_opt = new osgDB::Options;
00303 #endif
00304 
00305   //Check if file format is supported, get readerwriter
00306   osgDB::ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(file_ext);
00307   if (!rw)
00308   {
00309     std::cout << "Data file format " << file_ext << " not supported" << std::endl;
00310     return NULL;
00311   }
00312 
00313   //Try loading the resource,
00314 #if OSG_VERSION_MAJOR>=3
00315   osgDB::ReaderWriter::ReadResult readResult = rw->readNode( buffer,local_opt.get());
00316 #else
00317   osgDB::ReaderWriter::ReadResult readResult = rw->readNode(buffer);
00318 #endif
00319   if (readResult.validNode())
00320     return readResult.takeNode();
00321   else
00322     std::cout << "Can't load file " << name << std::endl;
00323   return NULL;
00324 
00325 }
00326 
00327 osg::Node * UWSimGeometry::loadGeometry(boost::shared_ptr<Geometry> geom)
00328 {
00329   if (geom->type == 0)
00330   {
00331     osg::Node * node = retrieveResource(geom->file);
00332     if (node == NULL)
00333     {
00334       //retrieve resource didn't succeed, let's search in the DATA PATH
00335       const std::string SIMULATOR_DATA_PATH = std::string(getenv("HOME")) + "/.uwsim/data";
00336 
00337       osgDB::Registry::instance()->getDataFilePathList().push_back(std::string(SIMULATOR_DATA_PATH));
00338       osgDB::Registry::instance()->getDataFilePathList().push_back(
00339           std::string(SIMULATOR_DATA_PATH) + std::string("/objects"));
00340       osgDB::Registry::instance()->getDataFilePathList().push_back(
00341           std::string(SIMULATOR_DATA_PATH) + std::string("/terrain"));
00342       osgDB::Registry::instance()->getDataFilePathList().push_back(
00343           std::string(UWSIM_ROOT_PATH) + std::string("/data/shaders"));
00344       node = osgDB::readNodeFile(geom->file);
00345 
00346       if (node == NULL)
00347       {
00348         std::cerr << "Error retrieving file " << geom->file
00349             << " Check URDF file or set your data path with the --dataPath option." << std::endl;
00350         exit(0);
00351       }
00352     }
00353     //If node isn't a group create a group with it.
00354     if (node->asGroup() == NULL)
00355     {
00356       osg::Node * aux = node;
00357       node = new osg::Group();
00358       node->asGroup()->addChild(aux);
00359     }
00360     return node;
00361   }
00362   else if (geom->type == 1)
00363   {
00364     return UWSimGeometry::createOSGBox(osg::Vec3(geom->boxSize[0], geom->boxSize[1], geom->boxSize[2]));
00365   }
00366   else if (geom->type == 2)
00367     return UWSimGeometry::createOSGCylinder(geom->radius, geom->length);
00368   else if (geom->type == 3)
00369     return UWSimGeometry::createOSGSphere(geom->radius);
00370   else if (geom->type == 4)
00371     return new osg::Group();
00372   std::cerr << "Unknown geometry type. " << std::endl;
00373   exit(0);
00374   return NULL;
00375 }
00376 
00377 getWorldCoordOfNodeVisitor::getWorldCoordOfNodeVisitor() :
00378     osg::NodeVisitor(NodeVisitor::TRAVERSE_PARENTS), done(false)
00379 {
00380   wcMatrix.reset(new osg::Matrixd());
00381 }
00382 
00383 void getWorldCoordOfNodeVisitor::apply(osg::Node &node)
00384 {
00385   if (!done)
00386   {
00387     if (0 == node.getNumParents())
00388     { // no parents
00389       wcMatrix->set(osg::computeLocalToWorld(this->getNodePath()));
00390       done = true;
00391     }
00392     traverse(node);
00393   }
00394 }
00395 
00396 boost::shared_ptr<osg::Matrix> getWorldCoordOfNodeVisitor::giveUpDaMat()
00397 {
00398   return wcMatrix;
00399 }
00400 
00401 boost::shared_ptr<osg::Matrix> getWorldCoords(osg::Node* node)
00402 {
00403   osg::ref_ptr<getWorldCoordOfNodeVisitor> ncv = new getWorldCoordOfNodeVisitor();
00404   if (node && ncv)
00405   {
00406     node->accept(*ncv);
00407     return ncv->giveUpDaMat();
00408   }
00409   else
00410   {
00411     return boost::shared_ptr<osg::Matrix>();
00412   }
00413 }
00414 
00415 
00416 //Dredging functions
00417 
00418 #include <osg/ComputeBoundsVisitor>
00419 #include "uwsim/SimulatedIAUV.h"
00420 
00421 DynamicHF::DynamicHF(osg::HeightField* height, boost::shared_ptr<osg::Matrix> mat, std::vector<boost::shared_ptr<AbstractDredgeTool> > tools)
00422 {
00423   dredgeTools=tools;
00424   heightField=height;
00425   objectMat=mat;
00426   mat->preMultRotate(heightField->getRotation());
00427 }
00428 
00429 //Checks if any dredge tool is in dredging distance and modifies the dynamicHF
00430 void DynamicHF::update( osg::NodeVisitor*,osg::Drawable* drawable )
00431 {
00432 
00433   for(unsigned int i=0;i<dredgeTools.size();i++)
00434   {
00435     boost::shared_ptr<osg::Matrix> dredgeToolmat=dredgeTools[i]->getDredgePosition();
00436 
00437     int modified=0;
00438     int nparticles=0;
00439 
00440     for (int r = 0; r < heightField->getNumRows(); r++)
00441     {
00442       for (int c = 0; c < heightField->getNumColumns(); c++)
00443       {
00444         //TODO: check a cone instead of a sphere, take the distance from dredgeTool
00445         if( (  dredgeToolmat->getTrans() - (objectMat->getTrans() + (heightField->getRotation().inverse() *heightField->getOrigin()) + 
00446           osg::Vec3(c*heightField->getXInterval(),r*heightField->getYInterval(),heightField->getHeight(c,r)))
00447           ).length2()< 0.01)
00448         {
00449            heightField->setHeight(c, r,heightField->getHeight(c,r)-0.01);
00450            modified=1;
00451            nparticles++;
00452         }
00453       }
00454     }
00455 
00456     if(modified)
00457     {
00458       drawable->dirtyDisplayList();
00459       drawable->dirtyBound();
00460     }
00461 
00462     dredgeTools[i]->dredgedParticles(nparticles);
00463     
00464   }
00465 }
00466 
00467 //Create a dynamic heightfield that can be dredged
00468 osg::Node* createHeightField(osg::ref_ptr<osg::Node> object, std::string texFile, double percent, const std::vector<boost::shared_ptr<SimulatedIAUV> >  vehicles)
00469 {
00470   osg::ComputeBoundsVisitor cbv;
00471   object->accept(cbv);
00472   osg::BoundingBox box = cbv.getBoundingBox();
00473 
00474   boost::shared_ptr<osg::Matrix> mat=getWorldCoords( object);
00475 
00476   box._min= mat->getRotate() * box._min;
00477   box._max= mat->getRotate() * box._max;
00478 
00479   //std::cout<<box.xMin()<<" "<<box.yMin()<<" "<<box.zMin()<<std::endl;
00480   //std::cout<<box.xMax()<<" "<<box.yMax()<<" "<<box.zMax()<<std::endl;
00481 
00482   //Adjust resolution to closest multiple for each axis
00483   float resX=0.01 + fmod((double)abs(box.xMax()-box.xMin()),0.01) / (double)floor(abs(box.xMax()-box.xMin())/0.01);
00484   float resY=0.01 + fmod((double)abs(box.yMax()-box.yMin()),0.01) / (double)floor(abs(box.yMax()-box.yMin())/0.01);
00485  
00486   //std::cout<<"Resolution: "<<resX<<" "<<resY<<std::endl;
00487   //std::cout<<"Nelems: "<<(abs(box.xMax()-box.xMin())/(resX)+1)<<" "<<(abs(box.yMax()-box.yMin())/(resY)+1)<<std::endl;
00488 
00489   int addedElems = abs(box.zMax()-box.zMin())*percent / 0.01*3;
00490      
00491   //Create heightfield
00492   osg::HeightField* heightField = new osg::HeightField();
00493   heightField->allocate(abs(box.xMax()-box.xMin())/(resX)+1+addedElems*2,abs(box.yMax()-box.yMin())/(resY)+1+addedElems*2);
00494   heightField->setOrigin(mat->getRotate().inverse() * osg::Vec3(min(box.xMin(),box.xMax())-addedElems*resX,min(box.yMin(),box.yMax())-addedElems*resY, min(box.zMin(),box.zMax())));
00495   heightField->setRotation(mat->getRotate().inverse());  //TODO: does not work with scales!
00496   heightField->setXInterval(resX);
00497   heightField->setYInterval(resY);
00498   heightField->setSkirtHeight(0.01f);
00499 
00500   //std::cout<<"Allocate: "<<(abs(box.xMax()-box.xMin())/(resX)+1)<<" "<<(abs(box.yMax()-box.yMin())/(resY)+1)<<std::endl;        
00501   //std::cout<<"Origin: "<<heightField->getOrigin().x()<<" "<<heightField->getOrigin().y()<<" "<<heightField->getOrigin().z()<<std::endl; 
00502   //std::cout<<"Height: "<<(box.zMax()-box.zMin())*percent<<std::endl;
00503 
00504   //set height
00505   for (int r = 0; r < heightField->getNumRows(); r++)
00506   {
00507     for (int c = 0; c < heightField->getNumColumns(); c++)
00508     {
00509       heightField->setHeight(c, r, abs(box.zMax()-box.zMin())*percent );
00510 
00511       if(r<addedElems)
00512        heightField->setHeight(c, r,min((double)heightField->getHeight(c,r), (1 - ( (addedElems-r)*(addedElems-r) / ((double)(addedElems)*(addedElems)))) * abs(box.zMax()-box.zMin())*percent) ); // r*resY) );  
00513 
00514       if(heightField->getNumRows()-r<addedElems)
00515        heightField->setHeight(c, r,min((double)heightField->getHeight(c,r),(1 - ( (addedElems-(heightField->getNumRows()-r))*(addedElems-(heightField->getNumRows()-r)) / ((double)(addedElems)*(addedElems)))) * abs(box.zMax()-box.zMin())*percent) );      //(heightField->getNumRows()-r)*resY) );  
00516 
00517       if(c<addedElems)
00518        heightField->setHeight(c, r,min((double)heightField->getHeight(c,r), (1 - ( (addedElems-c)*(addedElems-c) / ((double)(addedElems)*(addedElems)))) * abs(box.zMax()-box.zMin())*percent) ); //c*resX) );  
00519 
00520       if(heightField->getNumColumns()-c<addedElems)
00521        heightField->setHeight(c, r,min((double)heightField->getHeight(c,r), (1 - ( (addedElems-(heightField->getNumColumns()-c))*(addedElems-(heightField->getNumColumns()-c)) / ((double)(addedElems)*(addedElems)))) * abs(box.zMax()-box.zMin())*percent) ); //(heightField->getNumColumns()-c)*resX) );  
00522 
00523     }
00524   }
00525 
00526   //Search for dredge tools on vehicles,
00527   std::vector<boost::shared_ptr<AbstractDredgeTool> > dredgeTools;
00528   for(unsigned int i=0;i<vehicles.size();i++)
00529   {
00530     for(unsigned int j=0;j<vehicles[i]->devices->all.size();j++)
00531     {
00532       boost::shared_ptr<AbstractDredgeTool>  dredgeTool = boost::dynamic_pointer_cast < AbstractDredgeTool >  (vehicles[i]->devices->all[j]);
00533 
00534       if(dredgeTool)
00535         dredgeTools.push_back(dredgeTool);
00536     }
00537   }
00538 
00539 
00540   //Create the geode and add the updater
00541   osg::Geode* geode = new osg::Geode();
00542   osg::ShapeDrawable* draw=new osg::ShapeDrawable(heightField);
00543   geode->addDrawable(draw);
00544   DynamicHF * dynamicHF=new DynamicHF(heightField,mat, dredgeTools );
00545   draw->setUpdateCallback( dynamicHF);
00546      
00547   //Add the texture
00548   osg::Texture2D* tex = new osg::Texture2D(osgDB::readImageFile(texFile));
00549   tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
00550   tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
00551   geode->getOrCreateStateSet()->setTextureAttributeAndModes(0, tex);
00552 
00553   return geode;
00554 }
00555 


uwsim
Author(s): Mario Prats , Javier Perez
autogenerated on Fri Aug 28 2015 13:28:58