src/Terrain3D.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (C) 2007 by                                                 *
00003  *         Pierre-yves JEZEQUEL, Julien MICHOT, Loic MOISAN,               *
00004  *         Julien PAPILLON, Sebastien PINEAUD, Barthelemy SERRES           *
00005  *                                                                         *
00006  *   https://sourceforge.net/projects/anidam                               *
00007  *                                                                         *
00008  *   This program is free software; you can redistribute it and/or modify  *
00009  *   it under the terms of the GNU General Public License as published by  *
00010  *   the Free Software Foundation; either version 2 of the License, or     *
00011  *   (at your option) any later version.                                   *
00012  *                                                                         *
00013  *   This program is distributed in the hope that it will be useful,       *
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00016  *   GNU General Public License for more details.                          *
00017  *                                                                         *
00018  *   You should have received a copy of the GNU General Public License     *
00019  *   along with this program; if not, write to the                         *
00020  *   Free Software Foundation, Inc.,                                       *
00021  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00022  ***************************************************************************/
00023 #include "Terrain3D.h"
00024 #include <stdlib.h>
00025 #include <time.h>
00026 #include <osg/Billboard>
00027 #include <osg/AlphaFunc>
00028 #include <osg/BlendFunc>
00029 
00030 
00031 Terrain3D::Terrain3D(char* path, osg::Group* terrain,int nbTree)
00032 {
00033         float perc[4];
00034         // On lit le fichier contenant les hauteurs de la map
00035         osg::Image * heights = osgDB::readImageFile(path);
00036         if(!heights)
00037         {
00038                 printf("Erreur lors du chargement de la heigtmap\n");
00039                 ok = false;
00040                 return;
00041         }
00042 
00043         std::cout << "Heightmap chargée" << std::endl;
00044         
00045 
00046         // On alloue une heightmap
00047         HeightMap = new osg::HeightField();
00048         if(!HeightMap) 
00049         {
00050                 printf("Erreur lors de l'allocation de la heightmap\n");
00051                 ok = false;
00052                 return;
00053         }
00054 
00055         // On alloue un bitmap
00056         SDL_Surface* TextureFinale = SDL_CreateRGBSurface(SDL_HWSURFACE,heights->s(), heights->t(), 32, 0, 0, 0, 0);
00057         if(!TextureFinale)
00058         {
00059                 printf("Erreur lors de l'allocation de la texture de terrain\n");
00060                 ok = false;
00061                 return;
00062         }
00063 
00064         // On charge les différentes textures
00065 
00066                 // texture de prairie
00067         SDL_Surface* terre = SDL_LoadBMP("data/Heightmap/terre.bmp");
00068         if(!terre)
00069         {
00070                 printf("Erreur lors de la récupération de la texture de terre\n");
00071                 ok = false;
00072                 return;
00073         }
00074 
00075         SDL_Surface* prairies = SDL_LoadBMP("data/Heightmap/grassm.bmp");
00076         if(!prairies)
00077         {
00078                 printf("Erreur lors de la récupération de la texture de prairie\n");
00079                 ok = false;
00080                 return;
00081         }
00082 
00083         SDL_Surface* rocheuses = SDL_LoadBMP("data/Heightmap/rock.bmp");
00084         if(!rocheuses)
00085         {
00086                 printf("Erreur lors de la récupération de la texture de montagne\n");
00087                 ok = false;
00088                 return;
00089         }
00090 
00091         SDL_Surface* neige = SDL_LoadBMP("data/Heightmap/snow.bmp");
00092         if(!neige)
00093         {
00094                 printf("Erreur lors de la récupération de la texture de neige\n");
00095                 ok = false;
00096                 return;
00097         }
00098 
00099         std::cout << "Textures chargées" << std::endl;
00100         
00101         // On dimensionne la heightmap
00102         printf("Heightmap : width=%d\theight=%d\n", heights->s(), heights->t());
00103         HeightMap->allocate(heights->s(), heights->t());
00104 
00105         // On affecte toutes les hauteurs
00106         for(int row=0; row<heights->s(); row++)
00107                 for(int column=0; column<heights->t(); column++)
00108                 {
00109                         // Recuperation des participations de couleurs pour le pixel courant 
00110                         int tmpi =      (int ) (( ((float) column)/TextureFinale->h) * heights->s()) ;
00111                         int tmpj =      (int ) (( ((float) row)/TextureFinale->w) * heights->t()) ;
00112                         RemplitPerc(perc,heights->data(column,row)[0]);
00113 
00114                         // On recupere les couleurs 
00115                         tmpi = column%terre->h;
00116                         tmpj = row%terre->w;
00117 
00118                         double b = perc[0] * GetPixelColor(terre,tmpi,tmpj,0);
00119                         double g = perc[0] * GetPixelColor(terre,tmpi,tmpj,1);
00120                         double r = perc[0] * GetPixelColor(terre,tmpi,tmpj,2);
00121 
00122                         tmpi =  column%prairies->h;
00123                         tmpj =  row%prairies->w;
00124 
00125                         b += perc[1] * GetPixelColor(prairies,tmpi,tmpj,0);
00126                         g += perc[1] * GetPixelColor(prairies,tmpi,tmpj,1);
00127                         r += perc[1] * GetPixelColor(prairies,tmpi,tmpj,2);
00128 
00129                         tmpi =  column%rocheuses->h;
00130                         tmpj =  row%rocheuses->w;
00131 
00132                         b += perc[2] * GetPixelColor(rocheuses,tmpi,tmpj,0);
00133                         g += perc[2] * GetPixelColor(rocheuses,tmpi,tmpj,1);
00134                         r += perc[2] * GetPixelColor(rocheuses,tmpi,tmpj,2);
00135 
00136                         tmpi =  column%neige->h;
00137                         tmpj =  row%neige->w;
00138 
00139                         b += perc[3] * GetPixelColor(neige,tmpi,tmpj,0);
00140                         g += perc[3] * GetPixelColor(neige,tmpi,tmpj,1);
00141                         r += perc[3] * GetPixelColor(neige,tmpi,tmpj,2);
00142 
00143                         // On a notre composante RGB, on la place ensuite dans la texture de terrain
00144                         SetPixel(TextureFinale, row, column, SDL_MapRGB(TextureFinale->format, (unsigned char)r, (unsigned char)g, (unsigned char)b));
00145                         if(row==(heights->s()-1) ||column == (heights->t()-1) || row==0 || column==0)
00146                         {
00147                                 SetPixel(TextureFinale, row, column, SDL_MapRGB(TextureFinale->format, 0,0,0));
00148                                 HeightMap->setHeight(row, heights->t()-column-1, -10);
00149                         } else
00150                         // On affecte la hauteur au point de la heigtmap
00151                                 HeightMap->setHeight(row, heights->t()-column-1, heights->data(column,row)[0]);
00152                         
00153                 }
00154 
00155         std::cout << "Textures crées" << std::endl;
00156 
00157         // On sauvegarde la texture de terrain
00158         SDL_SaveBMP(TextureFinale, "data/Heightmap/TextTerrain.bmp");
00159 
00160 /*      osg::HeightField* HM = new osg::HeightField();
00161         HM->allocate((HeightMap->getNumColumns()*2)-1, (HeightMap->getNumRows()*2)-1);
00162         //HM->setXInterval(HM->getXInterval()/2);
00163         //HM->setYInterval(HM->getYInterval()/2);
00164         
00165         for(int row=0; row<(HeightMap->getNumRows()-1); row++)
00166                 for(int column=0; column<(HeightMap->getNumColumns()-1); column++)
00167                 {
00168                         HM->setHeight(row*2, column*2, HeightMap->getHeight(row,column));
00169                         HM->setHeight(1+row*2,column*2, (-HeightMap->getHeight(row,column)+HeightMap->getHeight(row+1,column))/2+HeightMap->getHeight(row,column));
00170                         HM->setHeight(row*2,1+column*2, (-HeightMap->getHeight(row,column)+HeightMap->getHeight(row,column+1))/2+HeightMap->getHeight(row,column));
00171                         HM->setHeight(1+row*2,1+column*2, (-HeightMap->getHeight(row,column)+HeightMap->getHeight(row+1,column+1))/2+HeightMap->getHeight(row,column));
00172                 }
00173         HeightMap = HM;*/
00174 
00175         // On place la HeightMap dans une Geode
00176         osg::ShapeDrawable * HMDrawable = new osg::ShapeDrawable(HeightMap);
00177         HeightMapGeode = new osg::Geode();
00178         HeightMapGeode->addDrawable(HMDrawable);
00179 
00180         // On la texture
00181         TextureHeightMap();
00182 
00183         std::cout << "Heightmap terminée. " << std::endl;
00184 
00185         if(nbTree>0)
00186         {
00187                 std::cout << "Génération des arbres. " << std::endl;
00188 
00189                 osg::Texture2D *treeTexture = new osg::Texture2D;
00190                 treeTexture->setImage(osgDB::readImageFile("data/Textures/Arbre3.png"));
00191                 
00192                 osg::Texture2D *treeTexture2 = new osg::Texture2D;
00193                 treeTexture2->setImage(osgDB::readImageFile("data/Textures/Arbre2.png"));
00194                 
00195                 osg::Texture2D *treeTexture3 = new osg::Texture2D;
00196                 treeTexture3->setImage(osgDB::readImageFile("data/Textures/falltree.png"));
00197                 
00198                 osg::Texture2D *treeTexture4 = new osg::Texture2D;
00199                 treeTexture4->setImage(osgDB::readImageFile("data/Textures/arbre.png"));
00200                 
00201                 osg::Texture2D *grassTexture = new osg::Texture2D;
00202                 grassTexture->setImage(osgDB::readImageFile("data/Textures/grass1.tga"));
00203                 
00204                 osg::Texture2D *grassTexture1 = new osg::Texture2D;
00205                 grassTexture1->setImage(osgDB::readImageFile("data/Textures/grass2.tga"));
00206                 
00207                 osg::Texture2D *grassTexture2 = new osg::Texture2D;
00208                 grassTexture2->setImage(osgDB::readImageFile("data/Textures/grass3.tga"));
00209                 
00210                 osg::Texture2D *grassTexture3 = new osg::Texture2D;
00211                 grassTexture3->setImage(osgDB::readImageFile("data/Textures/grass4.tga"));
00212                 
00213                 osg::Texture2D *grassTexture4 = new osg::Texture2D;
00214                 grassTexture4->setImage(osgDB::readImageFile("data/Textures/grass5.tga"));
00215 
00216                 // Puis on lui ajoute des arbres
00217                 srand(time(NULL));
00218 
00219                 
00220                 addTrees(terrain,grassTexture,2,1.0,1.1,nbTree,83,92);
00221                 addTrees(terrain,grassTexture1,2,1.0,1.0,nbTree,83,90);
00222                 addTrees(terrain,grassTexture2,2,2.0,1.0,nbTree,83,96);
00223                 addTrees(terrain,grassTexture4,2,1.0,1.0,nbTree,83,95);
00224                 addTrees(terrain,treeTexture,3,1.2,1.2,nbTree/2,90,125);
00225                 addTrees(terrain,treeTexture2,3,2.0,1.5,nbTree,83,120);
00226                 addTrees(terrain,treeTexture3,3,1.0,1.3,nbTree/4,85,110);
00227                 addTrees(terrain,treeTexture4,3,1.2,1.2,nbTree,83,110);
00228         }
00229         
00230         std::cout << "Génération terminée." << std::endl;
00231         ok = true;
00232 }
00233 
00234 void Terrain3D::RemplitPerc(float *perc, unsigned char haut)
00235 {
00236         /* On utilise la fonction rand pour mettre de l'aleatoire */
00237         int add = haut + (rand()%30)-15;
00238 
00239         if(add<0)
00240                 add = 0;
00241 
00242         if(add>255)
00243                 add = 255;
00244 
00245         haut = add;
00246         /* Que de la terre */
00247         if(haut<60)
00248         {
00249                 perc[0] = 1.0f;
00250                 perc[1] = 0.0f;
00251                 perc[2] = 0.0f;
00252                 perc[3] = 0.0f;
00253         }
00254         /* Mélange entre terre et prairie */
00255         else if(haut<80)
00256         {
00257                 perc[0] = 1.0f - (haut-60.0f)/20.0f;
00258                 perc[1] = (haut-60.0f)/20.0f;
00259                 perc[2] = 0.0f;
00260                 perc[3] = 0.0f;
00261         }
00262         /* Que de la prairie */
00263         else if(haut<100)
00264         {
00265                 perc[0] = 0.0f;
00266                 perc[1] = 1.0f;
00267                 perc[2] = 0.0f;
00268                 perc[3] = 0.0f;
00269         }
00270         /* Melange entre prairie et roche */
00271         else if(haut<150)
00272         {
00273                 perc[0] = 0.0f;
00274                 perc[1] = 1.0f - (haut-100.0f)/50.0f;
00275                 perc[2] = (haut-100.0f)/50.0f;
00276                 perc[3] = 0.0f;
00277         }
00278         /* Que de la roche */
00279         else if(haut<180)
00280         {
00281                 perc[0] = 0.0f;
00282                 perc[1] = 0.0f;
00283                 perc[2] = 1.0f;
00284                 perc[3] = 0.0f;
00285         }
00286         /* Melange entre roche et la neige */
00287         else if(haut<220)
00288         {
00289                 perc[0] = 0.0f;
00290                 perc[1] = 0.0f;
00291                 perc[2] = 1.0f - (haut-180.0f)/40.0f;
00292                 perc[3] = (haut-180.0f)/40.0f;
00293         }
00294         /* Que de la neige */
00295         else
00296         {
00297                 perc[0] = 0.0f;
00298                 perc[1] = 0.0f;
00299                 perc[2] = 0.0f;
00300                 perc[3] = 1.0f;
00301         }
00302 }
00303 
00304 unsigned char Terrain3D::GetPixelColor(SDL_Surface *image, int i, int j,int k)
00305 {
00306         Uint8 *p = (Uint8*)image->pixels + j * image->pitch + i * image->format->BytesPerPixel;
00307         unsigned char r, g, b;
00308         SDL_GetRGB(*(Uint32*)p,image->format,&r,&g,&b);
00309         switch(k)
00310         {
00311                 case 0:
00312                         return b;
00313                 case 1:
00314                         return g;
00315                 case 2:
00316                         return r;
00317                 default:
00318                         return 0;
00319         }
00320 }
00321 
00322 void Terrain3D::SetPixel(SDL_Surface* Surface, int x, int y, Uint32 pixel)
00323 {
00324     /* p est l'adresse du pixel que l'on veut modifier */
00325     Uint8 *p = (Uint8*)Surface->pixels + y * Surface->pitch + x * Surface->format->BytesPerPixel;
00326 
00327     switch(Surface->format->BytesPerPixel)
00328     {
00329     case 1:
00330         *p = pixel;
00331         break;
00332 
00333     case 2:
00334         *(Uint16*)p = pixel;
00335         break;
00336 
00337     case 3:
00338         if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
00339         {
00340             p[0] = (pixel >> 16) & 0xff;
00341             p[1] = (pixel >> 8) & 0xff;
00342             p[2] = pixel & 0xff;
00343         }
00344 
00345         else
00346         {
00347             p[0] = pixel & 0xff;
00348             p[1] = (pixel >> 8) & 0xff;
00349             p[2] = (pixel >> 16) & 0xff;
00350         }
00351         break;
00352 
00353     case 4:
00354         *(Uint32 *)p = pixel;
00355         break;
00356     }
00357 }
00358 
00359 void Terrain3D::TextureHeightMap()
00360 {
00361         // On charge la texture
00362         osg::Texture2D* GrassTex = new osg::Texture2D;
00363         GrassTex->setDataVariance(osg::Object::DYNAMIC);
00364 
00365         osg::Image* GrassImg = osgDB::readImageFile("Heightmap/TextTerrain.bmp");
00366         if (!GrassImg)
00367         {
00368                 std::cout << "Texture non trouvée" << std::endl;
00369                 ok = false;
00370                 return;
00371         }
00372 
00373         // Assign the texture to the image we read from file:
00374         GrassTex->setImage(GrassImg);
00375 
00376         // Create a new StateSet with default settings:
00377         osg::StateSet* stateGrass = new osg::StateSet();
00378 
00379         // Assign texture unit 0 of our new StateSet to the texture
00380         //  we just created and enable the texture.
00381         stateGrass->setTextureAttributeAndModes(0,GrassTex,osg::StateAttribute::ON);
00382 
00383         // Création de la lumière
00384         osg::Light* Lumiere = new osg::Light();
00385         Lumiere->setPosition(osg::Vec4(0,0,50,1));
00386         Lumiere->setAmbient(osg::Vec4(220,220,220,1));
00387         Lumiere->setDirection(osg::Vec3(0,-0.25,-1));
00388         Lumiere->setDiffuse(osg::Vec4(0,120,120,1));
00389         stateGrass->setAttributeAndModes(Lumiere);
00390 
00391         HeightMapGeode->setStateSet(stateGrass);
00392 }
00393 
00394 bool Terrain3D::Ok()
00395 {
00396         return ok;
00397 }
00398 
00399 osg::Geode* Terrain3D::getGeode()
00400 {
00401         return HeightMapGeode;
00402 }
00403 
00408 void Terrain3D::addTrees(osg::Group* rootNode, osg::Texture2D* treeTexture, float scale,float width,float height, int number, float hmin, float hmax)
00409 {
00410         
00411         osg::Billboard* shrubBillBoard = new osg::Billboard();
00412                                 
00413         shrubBillBoard->setMode(osg::Billboard::AXIAL_ROT);
00414         shrubBillBoard->setAxis(osg::Vec3(0.0f,0.0f,1.0f));
00415         shrubBillBoard->setNormal(osg::Vec3(0.0f,-1.0f,0.0f));
00416         
00417         
00418         osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
00419         alphaFunc->setFunction(osg::AlphaFunc::GEQUAL,0.5f);
00420         
00421         osg::StateSet* billBoardStateSet = new osg::StateSet;
00422         
00423         billBoardStateSet->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
00424         billBoardStateSet->setTextureAttributeAndModes(0, treeTexture, osg::StateAttribute::ON );
00425         billBoardStateSet->setAttributeAndModes
00426                         (new osg::BlendFunc, osg::StateAttribute::ON );
00427         osg::AlphaFunc* alphaFunction = new osg::AlphaFunc;
00428         alphaFunction->setFunction(osg::AlphaFunc::GEQUAL,0.05f);
00429         billBoardStateSet->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON );
00430 
00431         osg::Drawable* shrub1Drawable = createBillboardDrawable( scale, billBoardStateSet,width,height);
00432 
00433         // Add these drawables to our billboard at various positions
00434         
00435         for(int i=0; i<(number); i++)
00436         {
00437                 bool place = false;
00438                 while(!place)
00439                 {
00440                         int c = rand()%HeightMap->getNumColumns();
00441                         int r = rand()%HeightMap->getNumRows();
00442         
00443                         if((HeightMap->getHeight(c,r)>hmin) && (HeightMap->getHeight(c,r)<hmax) ||
00444                                                 r<160 && c<220&& (HeightMap->getHeight(c,r)>hmin-18) && (HeightMap->getHeight(c,r)<hmax))
00445                         {
00446                                 float x = HeightMap->getOrigin().x() + c * HeightMap->getXInterval();
00447                                 float y = HeightMap->getOrigin().y() + r * HeightMap->getYInterval();
00448                                 float z = HeightMap->getHeight(c,r);
00449                                 shrubBillBoard->addDrawable( shrub1Drawable , osg::Vec3(x,y,z) );
00450                                 place = true;
00451                         }
00452                 }
00453         }
00454         
00455         
00456         rootNode->addChild(shrubBillBoard);
00457         
00458         
00459 }
00460 
00461 osg::Drawable* Terrain3D::createBillboardDrawable(float scale, osg::StateSet* bbState,float width,float height)
00462 {
00463         // Taille du shrub
00464         //float width = 1.5f;
00465         //float height = 3.0f;
00466         
00467         // Mise à l'échelle
00468         width *= scale;
00469         height *= scale;
00470         
00471         // Initialisation d'un Geometry
00472         osg::Geometry* shrubQuad = new osg::Geometry;
00473         
00474         // Declare an array of vertices, assign values so we can create a
00475         // quadrilateral centered relative to the Z axis
00476         osg::Vec3Array* shrubVerts = new osg::Vec3Array(4);
00477         (*shrubVerts)[0] = osg::Vec3(-width/2.0f, 0, 0);
00478         (*shrubVerts)[1] = osg::Vec3( width/2.0f, 0, 0);
00479         (*shrubVerts)[2] = osg::Vec3( width/2.0f, 0, height);
00480         (*shrubVerts)[3] = osg::Vec3(-width/2.0f, 0, height);
00481         shrubQuad->setVertexArray(shrubVerts);
00482         
00483         // Déclaration et assignation des coordonnées texture
00484         osg::Vec2Array* shrubTexCoords = new osg::Vec2Array(4);
00485         (*shrubTexCoords)[0].set(0.0f,0.0f);
00486         (*shrubTexCoords)[1].set(1.0f,0.0f);
00487         (*shrubTexCoords)[2].set(1.0f,1.0f);
00488         (*shrubTexCoords)[3].set(0.0f,1.0f);
00489         shrubQuad->setTexCoordArray(0,shrubTexCoords);
00490         
00491         // Ajout de primitives (QUADS) au geometry
00492         shrubQuad->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
00493         
00494         // S'assurer que le géometry est dans l'état correct
00495         shrubQuad->setStateSet(bbState);
00496         
00497         return shrubQuad;
00498 }               

Generated on Tue Jun 5 16:56:48 2007 for Anidam by  doxygen 1.5.1