Visualization Library

A lightweight C++ OpenGL middleware for 2D/3D graphics
[Home] [Tutorials] [All Classes] [Grouped Classes]

X:/dropbox/visualizationlibrary/src/vlGraphics/Terrain.cpp

Go to the documentation of this file.
00001 /**************************************************************************************/
00002 /*                                                                                    */
00003 /*  Visualization Library                                                             */
00004 /*  http://www.visualizationlibrary.org                                               */
00005 /*                                                                                    */
00006 /*  Copyright (c) 2005-2010, Michele Bosi                                             */
00007 /*  All rights reserved.                                                              */
00008 /*                                                                                    */
00009 /*  Redistribution and use in source and binary forms, with or without modification,  */
00010 /*  are permitted provided that the following conditions are met:                     */
00011 /*                                                                                    */
00012 /*  - Redistributions of source code must retain the above copyright notice, this     */
00013 /*  list of conditions and the following disclaimer.                                  */
00014 /*                                                                                    */
00015 /*  - Redistributions in binary form must reproduce the above copyright notice, this  */
00016 /*  list of conditions and the following disclaimer in the documentation and/or       */
00017 /*  other materials provided with the distribution.                                   */
00018 /*                                                                                    */
00019 /*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
00020 /*  ANY 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
00023 /*  ANY 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 ON    */
00026 /*  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 
00032 #include <vlGraphics/Terrain.hpp>
00033 #include <vlGraphics/Actor.hpp>
00034 #include <vlGraphics/Effect.hpp>
00035 #include <vlGraphics/Geometry.hpp>
00036 #include <vlGraphics/GLSL.hpp>
00037 #include <vlGraphics/GeometryPrimitives.hpp>
00038 #include <vlCore/Log.hpp>
00039 #include <vlCore/Say.hpp>
00040 
00041 using namespace vl;
00042 
00043 void Terrain::init()
00044 {
00045   mChunks.clear();
00046 
00047   if (mWidth <= 0 || mHeight <= 0 || mDepth <= 0 || mDetailRepetitionCount <= 0)
00048   {
00049     Log::error(
00050         Say("Terrain initialization failed: invalid parameters.\n"
00051              "width = %n\n"
00052              "hieght = %n\n"
00053              "depth = %n\n"
00054              "detail repetition = %n\n")
00055         << mWidth << mHeight << mDepth << mDetailRepetitionCount
00056       );
00057     return;
00058   }
00059 
00060   if (useGLSL())
00061   {
00062     if(fragmentShader().empty() || vertexShader().empty())
00063     {
00064       Log::error("vertex shader or fragment shader not defined.\n");
00065       /*mFragmentShader = "/glsl/terrain.fs";
00066       mVertexShader   = "/glsl/terrain.vs";*/
00067       return;
00068     }
00069   }
00070 
00071   bool use_uniform_transform = false; // experimental only
00072 
00073   // Log::print("Loading detail texture... ");
00074   ref<Image> detail_img = detailTexture().empty() ? ref<Image>(NULL) : loadImage(detailTexture());
00075 
00076   // Log::print("Loading terrain texture... ");
00077   ref<Image> terrain_img = loadImage(terrainTexture());
00078 
00079   // Log::print("Loading heightmap... ");
00080   ref<Image> heightmap_img = loadImage(heightmapTexture());
00081 
00082   if ( (!detail_img && !detailTexture().empty()) || !terrain_img || !heightmap_img)
00083   {
00084     Log::error("Terrain initialization failed.\n");
00085     return;
00086   }
00087 
00088   double dx = width() / heightmap_img->width();
00089   double dz = depth() / heightmap_img->height();
00090 
00091   int x_subdivision = -1;
00092   for(int ch=1; ch<1024; ++ch)
00093   {
00094     for(int ts=128; ts<=1024*8; ts*=2)
00095     {
00096       // printf("[%d][%d]x = %d\n", ch, ts, ch*ts-ch+1);
00097       if (ch*ts-ch+1 == heightmap_img->width())
00098       {
00099         x_subdivision = ch;
00100         break;
00101       }
00102     }
00103   }
00104 
00105   int y_subdivision = -1;
00106   for(int ch=1; ch<1024; ++ch)
00107   {
00108     for(int ts=128; ts<=1024*8; ts*=2)
00109     {
00110       // printf("[%d][%d]y = %d\n", ch, ts, ch*ts-ch+1);
00111       if (ch*ts-ch+1 == heightmap_img->height())
00112       {
00113         y_subdivision = ch;
00114         break;
00115       }
00116     }
00117   }
00118 
00119   if ( x_subdivision == -1 )
00120   {
00121     Log::error("texture width must be of the type: cn*ts-cn+1 where cn=chunk-number, ts=texture-chunk-size\n");
00122     return;
00123   }
00124   if ( y_subdivision == -1 )
00125   {
00126     Log::error("texture height must be of the type: cn*ts-cn+1 where cn=chunk-number, ts=texture-chunk-size\n");
00127     return;
00128   }
00129 
00130   int xsize  =   (heightmap_img->width()  -1 + x_subdivision)/x_subdivision;
00131   int zsize  =   (heightmap_img->height() -1 + y_subdivision)/y_subdivision;
00132   int tx_xsize = (terrain_img->width()    -1 + x_subdivision)/x_subdivision;
00133   int tx_zsize = (terrain_img->height()   -1 + y_subdivision)/y_subdivision;
00134 
00135   float dtu  = float(1.0 / tx_xsize / 2.0);
00136   float dtv  = float(1.0 / tx_zsize / 2.0);
00137   float dtu2 = 1.0f - dtu;
00138   float dtv2 = 1.0f - dtv;
00139   float du   = float(1.0 / xsize / 2.0);
00140   float dv   = float(1.0 / zsize / 2.0);
00141   float du2  = 1.0f - du;
00142   float dv2  = 1.0f - dv;
00143 
00144   float detail_du   = detail_img ? float(1.0 / detail_img->width()  / 2.0) : 0;
00145   float detail_dv   = detail_img ? float(1.0 / detail_img->height() / 2.0) : 0;
00146   float detail_du2  = mDetailRepetitionCount - detail_du;
00147   float detail_dv2  = mDetailRepetitionCount - detail_dv;
00148 
00149   ref<Geometry> terr_tile;
00150   ref<GLSLProgram> glsl;
00151 
00152   ref<ArrayFloat2> tmap_uv = new ArrayFloat2; // texture map
00153   ref<ArrayFloat2> dmap_uv = detail_img ? new ArrayFloat2 : NULL; // detail texture map
00154   ref<ArrayFloat2> hmap_uv = new ArrayFloat2; // height map
00155   tmap_uv->resize( xsize * zsize );
00156   if(detail_img)
00157     dmap_uv->resize( xsize * zsize );
00158   hmap_uv->resize( xsize * zsize );
00159   for(int z=0; z<zsize; ++z)
00160   {
00161     for(int x=0; x<xsize; ++x)
00162     {
00163       float u = (float)x/(xsize-1); // 0 .. 1
00164       float v = (float)z/(zsize-1); // 0 .. 1
00165       tmap_uv->at(x + z*xsize).s() = (1.0f-u) * dtu + u * dtu2;
00166       tmap_uv->at(x + z*xsize).t() = (1.0f-v) * dtv + v * dtv2;
00167       if (detail_img)
00168       {
00169         dmap_uv->at(x + z*xsize).s() = (1.0f-u) * detail_du + u * detail_du2;
00170         dmap_uv->at(x + z*xsize).t() = (1.0f-v) * detail_dv + v * detail_dv2;
00171       }
00172       hmap_uv->at(x + z*xsize).s() = (1.0f-u) * du + u * du2;
00173       hmap_uv->at(x + z*xsize).t() = (1.0f-v) * dv + v * dv2;
00174     }
00175   }
00176 
00177   if (useGLSL())
00178   {
00179     terr_tile = vl::makeGrid( vec3(0,0,0), 1.0f, 1.0f, xsize, zsize);
00180     terr_tile->setTexCoordArray(0, tmap_uv.get());
00181     terr_tile->setTexCoordArray(1, dmap_uv.get());
00182     terr_tile->setTexCoordArray(2, hmap_uv.get());
00183 
00184     glsl = new GLSLProgram;
00185     ref<Uniform> Height = new vl::Uniform("Height");
00186     Height->setUniformF((float)height());
00187     glsl->setUniform( Height.get() );
00188     glsl->attachShader( new GLSLFragmentShader( String::loadText(fragmentShader()) ) );
00189     if (use_uniform_transform)
00190       glsl->attachShader( new GLSLVertexShader( String::loadText("/glsl/terrain_ut.vs") ) );
00191     else
00192       glsl->attachShader( new GLSLVertexShader( String::loadText(vertexShader()) ) );
00193 
00194     // setup GLSL program 'static' uniforms
00195     ref<Uniform> terrain_tex   = new Uniform("terrain_tex");
00196     ref<Uniform> detail_tex    = new Uniform("detail_tex");
00197     ref<Uniform> heightmap_tex = new Uniform("heightmap_tex");
00198     terrain_tex  ->setUniformI(0);
00199     detail_tex   ->setUniformI(1);
00200     heightmap_tex->setUniformI(2);
00201     glsl->setUniform(terrain_tex.get());
00202     if (!detailTexture().empty())
00203       glsl->setUniform(detail_tex.get());
00204     glsl->setUniform(heightmap_tex.get());
00205 
00206     AABB aabb;
00207     aabb.setMinCorner((real)-0.5, 0, (real)-0.5);
00208     aabb.setMaxCorner((real)+0.5, (real)height(), (real)+0.5);
00209     terr_tile->setBoundingBox( aabb );
00210     terr_tile->setBoundingSphere(aabb);
00211     terr_tile->setBoundsDirty(false);
00212 
00213     shaderNode()->setRenderState(IN_Propagate, glsl.get());
00214   }
00215 
00216   shaderNode()->setEnable(EN_CULL_FACE, true);
00217   shaderNode()->setEnable(EN_DEPTH_TEST,true);
00218 
00219   if (!useGLSL())
00220   {
00221     ref<TexEnv> texenv = new TexEnv;
00222     texenv->setMode(TEM_MODULATE);
00223     shaderNode()->setRenderState(IN_Propagate, texenv.get(), 1);
00224   }
00225 
00226   // generate chunks
00227   for(int mz=0, tz=0; mz<heightmap_img->height()-1; mz+=zsize-1, tz+=tx_zsize-1)
00228   {
00229     for(int mx=0, tx=0; mx<heightmap_img->width()-1; mx+=xsize-1, tx+=tx_xsize-1)
00230     {
00231       // effect settings for this tile
00232       ref<Effect> terr_fx = new Effect;
00233       ref<ShaderNode> shader_node = new ShaderNode;
00234       shaderNode()->addChild(shader_node.get());
00235       shader_node->setShader(terr_fx->shader());
00236 
00237       // terrain texture
00238       ref<Image> tex_image = terrain_img->subImage(tx, tz, tx_xsize, tx_zsize);
00239       ref<TextureSampler> tex_unit0 = new TextureSampler;
00240       shader_node->setRenderState(IN_Propagate, tex_unit0.get(), 0);
00241       tex_unit0->setTexture(new Texture(tex_image.get(), terrainTextureFormat(), false));
00242       tex_unit0->texture()->getTexParameter()->setMagFilter(TPF_LINEAR);
00243       tex_unit0->texture()->getTexParameter()->setMinFilter(TPF_LINEAR);
00244       tex_unit0->texture()->getTexParameter()->setWrapS(TPW_REPEAT);
00245       tex_unit0->texture()->getTexParameter()->setWrapT(TPW_REPEAT);
00246 
00247       // detail texture
00248       if (detail_img)
00249       {
00250         ref<TextureSampler> tex_unit1 = new TextureSampler;
00251         shader_node->setRenderState(IN_Propagate, tex_unit1.get(), 1);
00252         tex_unit1->setTexture(new Texture(detail_img.get(), detailTextureFormat(), true));
00253         tex_unit1->texture()->getTexParameter()->setMagFilter(TPF_LINEAR);
00254         tex_unit1->texture()->getTexParameter()->setMinFilter(TPF_LINEAR_MIPMAP_LINEAR);
00255         tex_unit1->texture()->getTexParameter()->setWrapS(TPW_REPEAT);
00256         tex_unit1->texture()->getTexParameter()->setWrapT(TPW_REPEAT);
00257         if (Has_GL_EXT_texture_filter_anisotropic)
00258         {
00259           float max = 1.0f;
00260           glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max);
00261           tex_unit1->texture()->getTexParameter()->setAnisotropy(max);
00262         }
00263       }
00264 
00265       // heightmap texture
00266       ref<Image> hmap_image = heightmap_img->subImage(mx, mz, xsize, zsize);
00267       if (useGLSL())
00268       {
00269         ref<TextureSampler> tex_unit2 = new TextureSampler;
00270         shader_node->setRenderState(IN_Propagate, tex_unit2.get(), 2);
00271         tex_unit2->setTexture(new Texture(hmap_image.get(), heightmapTextureFormat(), false));
00272         tex_unit2->texture()->getTexParameter()->setMagFilter(TPF_NEAREST);
00273         tex_unit2->texture()->getTexParameter()->setMinFilter(TPF_NEAREST);
00274         tex_unit2->texture()->getTexParameter()->setWrapS(TPW_REPEAT);
00275         tex_unit2->texture()->getTexParameter()->setWrapT(TPW_REPEAT);
00276       }
00277 
00278       // compute tile transform
00279       dmat4 dmat;
00280       dmat.scale((xsize-1)*dx, 1.0, (zsize-1)*dz);
00281       dmat.translate(mx*dx + (xsize-1)*dx*0.5 - width()/2.0, 0, mz*dz + (zsize-1)*dz*0.5 - depth()/2.0);
00282       dmat.translate((dvec3)mOrigin);
00283       ref<Transform> transform = new Transform;
00284       transform->setLocalAndWorldMatrix((mat4)dmat);
00285 
00286       if (!useGLSL())
00287       {
00288         terr_tile = vl::makeGrid( vec3(0,0,0), 1.0f, 1.0f, xsize, zsize);
00289         terr_tile->setTexCoordArray(0, tmap_uv.get());
00290         terr_tile->setTexCoordArray(1, dmap_uv.get());
00291 
00292         ref<ArrayFloat3> verts = terr_tile->vertexArray()->as<ArrayFloat3>(); VL_CHECK(verts.get());
00293         for(int z=0; z<zsize; ++z)
00294         {
00295           for(int x=0; x<xsize; ++x)
00296           {
00297             fvec4 sample = hmap_image->sample(x,z) * (float)height();
00298             int index = x + xsize * z;
00299             verts->at(index).y() = sample.r();
00300           }
00301         }
00302       }
00303 
00304       // collect the terrain chunks to be inserted later in the ActorKdTree
00305 
00306       ref<Actor> actor = new Actor(terr_tile.get(), terr_fx.get(), (use_uniform_transform && useGLSL())?NULL:transform.get());
00307       mChunks.push_back(actor.get());
00308 
00309       if (use_uniform_transform && useGLSL())
00310       {
00311         #if 1
00312           actor->setUniformSet( new UniformSet );
00313           ref<Uniform> uniform_matrix = new Uniform("matrix");
00314           uniform_matrix->setUniform( (fmat4)dmat );
00315           actor->setUniform( uniform_matrix.get() );
00316         #else
00317           ref<Uniform> uniform_matrix = new Uniform("matrix");
00318           uniform_matrix->setUniform( (mat4)dmat );
00319           terr_fx->shader()->setUniform( uniform_matrix.get() );
00320         #endif
00321       }
00322 
00323       #if 0 // for debuggin purposes
00324         // terr_fx->shader()->gocMaterial()->setColorMaterialEnabled(true);
00325         terr_tile->setColorArray( fvec4(rand()%256/255.0f,rand()%256/255.0f,rand()%256/255.0f) );
00326       #endif
00327     }
00328   }
00329 
00330   shaderNode()->updateHierarchy();
00331   tree()->buildKdTree(mChunks);
00332 }

Visualization Library 2011.09.1160 Reference Documentation
Copyright 2005-2011 Michele Bosi. All rights reserved.
Updated on Thu May 2 2013 13:40:41.
Permission is granted to use this page to write and publish articles regarding Visualization Library.