Visualization Library v1.0.3A lightweight C++ OpenGL middleware for 2D/3D graphics |
[Download] [Tutorials] [All Classes] [Grouped Classes] |
00001 /**************************************************************************************/ 00002 /* */ 00003 /* Visualization Library */ 00004 /* http://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 }