OSGQuadTreeTerrain.cpp

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------------*\
00002  *                                OpenSG                                     *
00003  *                                                                           *
00004  *                                                                           *
00005  *               Copyright (C) 2000-2002 by the OpenSG Forum                 *
00006  *                                                                           *
00007  *                            www.opensg.org                                 *
00008  *                                                                           *
00009  *   contact: dirk@opensg.org, gerrit.voss@vossg.org, jbehr@zgdv.de          *
00010  *                                                                           *
00011 \*---------------------------------------------------------------------------*/
00012 /*---------------------------------------------------------------------------*\
00013  *                                License                                    *
00014  *                                                                           *
00015  * This library is free software; you can redistribute it and/or modify it   *
00016  * under the terms of the GNU Library General Public License as published    *
00017  * by the Free Software Foundation, version 2.                               *
00018  *                                                                           *
00019  * This library is distributed in the hope that it will be useful, but       *
00020  * WITHOUT ANY WARRANTY; without even the implied warranty of                *
00021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
00022  * Library General Public License for more details.                          *
00023  *                                                                           *
00024  * You should have received a copy of the GNU Library General Public         *
00025  * License along with this library; if not, write to the Free Software       *
00026  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                 *
00027  *                                                                           *
00028 \*---------------------------------------------------------------------------*/
00029 /*---------------------------------------------------------------------------*\
00030  *                                Changes                                    *
00031  *                                                                           *
00032  *                                                                           *
00033  *                                                                           *
00034  *                                                                           *
00035  *                                                                           *
00036  *                                                                           *
00037 \*---------------------------------------------------------------------------*/
00038
00039 /* Stefan Roettgers Quadtree Tesselation Algorithm
00040     http://wwwvis.informatik.uni-stuttgart.de/~roettger/data/Papers/TERRAIN.PDF
00041    This code is based on a Java3D Implementation by Martin Barbisch, 
00042    University Stuttgart
00043    http://wwwvis.informatik.uni-stuttgart.de/javatevi/
00044  */
00045
00046 #if __GNUC__ >= 4 || __GNUC_MINOR__ >=3
00047 #pragma GCC diagnostic warning "-Wsign-compare"
00048 #endif
00049 
00050 #ifdef WIN32
00051 #pragma warning( disable : 4018 )
00052 #endif
00053 
00054 //---------------------------------------------------------------------------
00055 //  Includes
00056 //---------------------------------------------------------------------------
00057
00058 #include <cstdlib>
00059 #include <cstdio>
00060 #include <sstream>
00061
00062 #include "OSGConfig.h"
00063
00064 #include "OSGQuadTreeTerrain.h"
00065 #include "OSGImage.h"
00066 #include "OSGRenderAction.h"
00067 #include "OSGMaterial.h"
00068 #include "OSGChunkMaterial.h"
00069 #include "OSGTextureObjChunk.h"
00070 #include "OSGTextureEnvChunk.h"
00071 #include "OSGSimpleSHLVariableChunk.h"
00072 #include "OSGTypedGeoIntegralProperty.h"
00073
00074 OSG_USING_NAMESPACE
00075
00076 // min global res., the higher the more vertices
00077 static const Real32 MinGlobalRes  = 10.0f;
00078 static const UInt32
00079     NW = 0,
00080     W = 1,
00081     SW = 2,
00082     S = 3,
00083     SE = 4,
00084     E = 5,
00085     NE = 6,
00086     N = 7,
00087     C = 8;
00088
00089 // switch to fill Geometry with TRIANGLE_FANS instead of TRIANGLES
00090 //#define WITH_TRIANGLE_FANS
00091
00092 // switch to share a single SimpleSHLChunk with several SimpleSHLParameterChunks
00093 //#define WITH_SINGLE_SHLCHUNK
00094
00095 // Documentation for this class is emited in the
00096 // OSGQuadTreeTerrainBase.cpp file.
00097 // To modify it, please change the .fcd file (OSGQuadTreeTerrain.fcd) and
00098 // regenerate the base file.
00099
00100 /***************************************************************************\
00101  *                           Class variables                               *
00102 \***************************************************************************/
00103
00104 /***************************************************************************\
00105  *                           Class methods                                 *
00106 \***************************************************************************/
00107
00108 void QuadTreeTerrain::initMethod(InitPhase ePhase)
00109 {
00110     Inherited::initMethod(ePhase);
00111
00112     if(ePhase == TypeObject::SystemPost)
00113     {
00114         RenderAction::registerEnterDefault(
00115             QuadTreeTerrain::getClassType(),
00116             reinterpret_cast<Action::Callback>(
00117                 &QuadTreeTerrain::renderEnter));
00118
00119         RenderAction::registerLeaveDefault(
00120             QuadTreeTerrain::getClassType(),
00121             reinterpret_cast<Action::Callback>(
00122                 &QuadTreeTerrain::renderActionLeaveHandler));
00123     }
00124 }
00125
00126
00127 /***************************************************************************\
00128  *                           Instance methods                              *
00129 \***************************************************************************/
00130
00131 /*-------------------------------------------------------------------------*\
00132  -  private                                                                 -
00133 \*-------------------------------------------------------------------------*/
00134
00135 /*----------------------- constructors & destructors ----------------------*/
00136
00137 QuadTreeTerrain::QuadTreeTerrain(void) :
00138     Inherited()
00139 {
00140 }
00141
00142 QuadTreeTerrain::QuadTreeTerrain(const QuadTreeTerrain &source) :
00143     Inherited(source)
00144 {
00145 }
00146
00147 QuadTreeTerrain::~QuadTreeTerrain(void)
00148 {
00149 }
00150
00151 Real32 QuadTreeTerrain::getHeightDataScaled (UInt32 i) const
00152 {
00153    switch(getHeightData()->getDataType())
00154    {
00155        default:
00156        case Image::OSG_INVALID_IMAGEDATATYPE:
00157        case Image::OSG_UINT8_IMAGEDATA:
00158
00159            return
00160                (reinterpret_cast<const UInt8 *>(
00161                    getHeightData()->getData()))[i] /
00162                Real32(TypeTraits<UInt8>::getMax());
00163
00164        case Image::OSG_UINT16_IMAGEDATA:
00165
00166            return
00167                (reinterpret_cast<const UInt16 *>(
00168                    getHeightData()->getData()))[i] /
00169                Real32(TypeTraits<UInt16>::getMax());
00170
00171        case Image::OSG_UINT32_IMAGEDATA:
00172            return
00173                (reinterpret_cast<const UInt32 *>(
00174                    getHeightData()->getData()))[i] /
00175                Real32(TypeTraits<UInt32>::getMax());
00176
00177        case Image::OSG_FLOAT16_IMAGEDATA:
00178            return (reinterpret_cast<const Real16 *>(
00179                        getHeightData()->getData()))[i];
00180
00181        case Image::OSG_FLOAT32_IMAGEDATA:
00182            return (reinterpret_cast<const Real32 *>(
00183                        getHeightData()->getData()))[i];
00184    };
00185 }
00186
00187 void QuadTreeTerrain::getVertex(UInt32 i, Pnt3f &point) const
00188 {
00189     GeoPnt3fProperty *pos =
00190         dynamic_cast<GeoPnt3fProperty *>(getHeightVertices());
00191
00192    point.setValue(pos->getField()[i]);
00193 }
00194 const Pnt3f &QuadTreeTerrain::getVertex(UInt32 i) const
00195 {
00196    GeoPnt3fProperty *pos =
00197        dynamic_cast<GeoPnt3fProperty *>(getHeightVertices());
00198
00199    return pos->getField()[i];
00200 }
00201 UInt32 QuadTreeTerrain::getNumVertices(void) const
00202 {
00203    GeoPnt3fProperty *pos =
00204        dynamic_cast<GeoPnt3fProperty *>(getHeightVertices());
00205
00206    return pos->getField().size();
00207 }
00208
00209 // GLSL programs
00210
00211 // vertex shader program for bump mapping in surface local coordinates
00212 static std::string _vp_program =
00213 "const vec3   planenormal   = vec3 (0.0, 0.0, 1.0);\n"
00214 "const vec3   planebinormal = vec3 (0.0, 1.0, 0.0);\n"
00215 "const vec3   planetangent  = vec3 (1.0, 0.0, 0.0);\n"
00216 "varying vec3 lightDir;    // interpolated surface local coordinate light direction\n"
00217 "varying vec3 viewDir;     // interpolated surface local coordinate view direction\n"
00218
00219 "void main(void)\n"
00220 "{\n"
00221 "    // Do standard vertex stuff\n"
00222
00223 "    gl_Position    = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
00224 "    gl_TexCoord[0] = gl_MultiTexCoord0;\n"
00225
00226 "    // Compute the binormal\n"
00227 "    vec3 n = normalize(gl_NormalMatrix * planenormal);\n"
00228 "    vec3 t = normalize(gl_NormalMatrix * planetangent);\n"
00229 "    vec3 b = cross(n, t);\n"
00230
00231 "    // Transform light position into surface local coordinates\n"
00232 "    vec3 LightPosition = gl_LightSource[0].position.xyz;\n"
00233
00234 "    vec3 v;\n"
00235 "    v.x = dot(LightPosition, t);\n"
00236 "    v.y = dot(LightPosition, b);\n"
00237 "    v.z = dot(LightPosition, n);\n"
00238
00239 "    lightDir = normalize(v);\n"
00240
00241 "    vec3 pos = vec3 (gl_ModelViewMatrix * gl_Vertex);\n"
00242 "    v.x = dot(pos, t);\n"
00243 "    v.y = dot(pos, b);\n"
00244 "    v.z = dot(pos, n);\n"
00245
00246 "    viewDir = normalize(v);\n"
00247 "\n"
00248 "}\n";
00249
00250 // fragment shader program for bump mapping in surface local coordinates
00251 static std::string _fp_program =
00252 "uniform sampler2D texSampler;\n"
00253 "uniform sampler2D nmapSampler;\n"
00254 "uniform float offsetS;\n"
00255 "uniform float offsetT;\n"
00256 "uniform float scaleS;\n"
00257 "uniform float scaleT;\n"
00258 "uniform float specularExponent;\n"
00259 "uniform float diffuseFactor;\n"
00260 "uniform float specularFactor;\n"
00261 "uniform vec3  basecolor;\n"
00262 "varying vec3 lightDir;       // interpolated surface local coordinate light direction\n"
00263 "varying vec3 viewDir;        // interpolated surface local coordinate view direction\n"
00264
00265 "void main (void)\n"
00266 "{\n"
00267 "    vec3 norm;\n"
00268 "    vec3 r;\n"
00269 "    vec3 color;\n"
00270 "    vec3 texcolor;\n"
00271 "    float intensity;\n"
00272 "    float spec;\n"
00273 "    float d;\n"
00274 "    // Fetch normal from normal map\n"
00275 "    vec2 nmapST = vec2 (gl_TexCoord[0])*vec2(scaleS, scaleT) + vec2(offsetS, offsetT);\n"
00276 "    norm = vec3(texture2D(nmapSampler, nmapST));\n"
00277 "    norm = (norm - 0.5) * 2.0;\n"
00278 "    // Compute diffuse reflection\n"
00279 "    d = dot(lightDir, norm);\n"
00280 "    intensity = max(d, 0.0) * diffuseFactor;\n"
00281 "    // Compute specular reflection\n"
00282 "    d = 2.0 * d;\n"
00283 "    r = d * norm;\n"
00284 "    r = lightDir - r;\n"
00285 "    spec = pow(max(dot(r, viewDir), 0.0), specularExponent) * specularFactor;\n"
00286 "    intensity += min (spec, 1.0);\n"
00287 "    // mix texture color and lighting color\n"
00288 "    texcolor = vec3(texture2D(texSampler, vec2 (gl_TexCoord[0])));\n"
00289 "    color = clamp(texcolor * basecolor * intensity, 0.0, 1.0);\n"
00290 "    // Write out final fragment color\n"
00291 "    gl_FragColor = vec4 (color, 1.0);\n"
00292 "\n"
00293 "}\n";
00294
00295 // SHLChunk initialized in QuadTreeTerrain::changed
00296 static SimpleSHLChunkMTRecPtr s_shlChunk;
00297
00298 SimpleSHLChunkTransitPtr QuadTreeTerrain::createSHLChunk () const
00299 {
00300    SimpleSHLChunkTransitPtr shl = SimpleSHLChunk::create();
00301
00302    shl->setVertexProgram  (_vp_program);
00303    shl->setFragmentProgram(_fp_program);
00304
00305    // CF without the following addRefCP, 
00306    // it crashes on changing SHLParameterChunks in Terrain::addMaterialChunks
00307
00308    //addRefX(shl);
00309
00310    return shl;
00311 }
00312
00313
00314 // ARB vertex and fragment programs
00315 // generated out of GLSL programs by 
00316 // cgc -oglsl -strict -profile arbvp1 vertex.glsl   -o perpixel.avp
00317 // cgc -oglsl -strict -profile arbfp1 fragment.glsl -o perpixel.afp
00318
00319 // CF does not work currently!
00320
00321 static std::string _avp_program =
00322 "!!ARBvp1.0\n"
00323 "PARAM mv[4]    = { state.matrix.modelview };\n"
00324 "PARAM mvp[4]   = { state.matrix.mvp };\n"
00325 "PARAM mvinv[4] = { state.matrix.modelview.invtrans };\n"
00326 "PARAM light0   = state.light[0].position;\n"
00327 "TEMP R0;\n"
00328 "TEMP R1;\n"
00329 "TEMP R2;\n"
00330 "TEMP R3;\n"
00331 "TEMP R4;\n"
00332 "TEMP R5;\n"
00333 "MOV result.texcoord[0], vertex.texcoord[0];\n"
00334 "MUL R0, vertex.position.y, mvp[1];\n"
00335 "//MUL R3.xyz, vertex.position.y, mv[1];\n"
00336 "//MOV R2.xyz, mvinv[0];\n"
00337 "//MOV R1.xyz, mvinv[2];\n"
00338 "//DP3 R1.w, mvinv[2], R1;\n"
00339 "//DP3 R2.x, mvinv[0], R2;\n"
00340 "//MAD R1.xyz, vertex.position.x, mv[0], R3;\n"
00341 "MAD R0, vertex.position.x, mvp[0], R0;\n"
00342 "MAD R0, vertex.position.z, mvp[2], R0;\n"
00343 "//MAD R1.xyz, vertex.position.z, mv[2], R1;\n"
00344 "//RSQ R2.x, R2.x;\n"
00345 "//MAD R4.xyz, vertex.position.w, mv[3], R1;\n"
00346 "//RSQ R1.x, R1.w;\n"
00347 "//MUL R2.xyz, R2.x, mvinv[0];\n"
00348 "//MUL R1.xyz, R1.x, mvinv[2];\n"
00349 "//DP3 R5.z, R4, R1;\n"
00350 "//DP3 R5.x, R4, R2;\n"
00351 "//DP3 R1.w, R1, light0;\n"
00352 "//MUL R3.xyz, R1.zxyw, R2.yzxw;\n"
00353 "//MAD R3.xyz, R1.yzxw, R2.zxyw, -R3;\n"
00354 "//DP3 R1.x, R2, light0;\n"
00355 "//DP3 R1.y, R3, light0;\n"
00356 "//DP3 R5.y, R4, R3;\n"
00357 "//DP3 R1.z, R1.xyww, R1.xyww;\n"
00358 "//DP3 R2.x, R5, R5;\n"
00359 "//RSQ R1.z, R1.z;\n"
00360 "MAD result.position, vertex.position.w, mvp[3], R0;\n"
00361 "//RSQ R0.x, R2.x;\n"
00362 "//MUL result.texcoord[1].xyz, R1.z, R1.xyww;\n"
00363 "//MUL result.texcoord[2].xyz, R0.x, R5;\n"
00364 "END\n"
00365 "# 31 instructions, 6 R-regs\n";
00366
00367 VertexProgramChunkTransitPtr QuadTreeTerrain::createVPChunk () const
00368 {
00369    std::istringstream vpcode(_avp_program.c_str());
00370
00371    VertexProgramChunkTransitPtr vp = VertexProgramChunk::create();
00372    vp->read(vpcode);
00373
00374    // parameter come here
00375
00376    return vp;
00377 }
00378
00379 static std::string _afp_program =
00380 "!!ARBfp1.0\n"
00381 "PARAM c[9] = { program.local[0..4], { 0.5, 2, 0, 1 }, program.local[6..8] };\n"
00382 "TEMP R0;\n"
00383 "TEMP R1;\n"
00384 "MOV R0.w, c[4].x;\n"
00385 "MOV R0.z, c[3].x;\n"
00386 "MOV R0.x, c[1];\n"
00387 "MOV R0.y, c[2].x;\n"
00388 "MAD R0.xy, fragment.texcoord[0], R0, R0.zwzw;\n"
00389 "TEX R0.xyz, R0, texture[1], 2D;\n"
00390 "ADD R0.xyz, R0, -c[5].x;\n"
00391 "MUL R1.xyz, R0, c[5].y;\n"
00392 "DP3 R0.x, fragment.texcoord[1], R1;\n"
00393 "MUL R1.xyz, R0.x, R1;\n"
00394 "MAD R1.xyz, -R1, c[5].y, fragment.texcoord[1];\n"
00395 "DP3 R0.y, R1, fragment.texcoord[2];\n"
00396 "MAX R0.y, R0, c[5].z;\n"
00397 "POW R0.y, R0.y, c[7].x;\n"
00398 "MUL R0.y, R0, c[8].x;\n"
00399 "MIN R1.x, R0.y, c[5].w;\n"
00400 "MAX R0.w, R0.x, c[5].z;\n"
00401 "TEX R0.xyz, fragment.texcoord[0], texture[0], 2D;\n"
00402 "MAD R0.w, R0, c[6].x, R1.x;\n"
00403 "MUL R0.xyz, R0, c[0];\n"
00404 "MUL_SAT result.color.xyz, R0, R0.w;\n"
00405 "MOV result.color.w, c[5];\n"
00406 "MOV result.color, c[5];\n"
00407 "END\n"
00408 "# 22 instructions, 2 R-regs\n";
00409
00410 FragmentProgramChunkTransitPtr QuadTreeTerrain::createFPChunk () const
00411 {
00412    std::istringstream fpcode(_afp_program.c_str());
00413
00414    FragmentProgramChunkTransitPtr fp = FragmentProgramChunk::create();
00415
00416    fp->read(fpcode);
00417    // parameter come here
00418    fp->addParameter("offsetS",
00419                     3,
00420                     Vec4f(1.0f/getTexSpacing(),
00421                           1.0f/getTexSpacing(),
00422                           1.0f/getTexSpacing(),
00423                           1.0f/getTexSpacing()));
00424
00425    fp->addParameter("scaleS",  1,
00426                     Vec4f(-getOriginTexX()/getTexSpacing(),
00427                           -getOriginTexX()/getTexSpacing(),
00428                           -getOriginTexX()/getTexSpacing(),
00429                           -getOriginTexX()/getTexSpacing()));
00430
00431    if(getTexYSpacing() != 1.0f)
00432    {
00433        fp->addParameter("offsetT",
00434                         4,
00435                         Vec4f(-getOriginTexY()/getTexYSpacing(),
00436                               -getOriginTexY()/getTexYSpacing(),
00437                               -getOriginTexY()/getTexYSpacing(),
00438                               -getOriginTexY()/getTexYSpacing()));
00439
00440        fp->addParameter("scaleT",
00441                         2,
00442                         Vec4f(1.0f/getTexYSpacing(),
00443                               1.0f/getTexYSpacing(),
00444                               1.0f/getTexYSpacing(),
00445                               1.0f/getTexYSpacing()));
00446    }
00447    else
00448    {
00449        fp->addParameter("offsetT",
00450                         4, Vec4f(-getOriginTexY()/getTexSpacing(),
00451                                  -getOriginTexY()/getTexSpacing(),
00452                                  -getOriginTexY()/getTexSpacing(),
00453                                  -getOriginTexY()/getTexSpacing()));
00454
00455        fp->addParameter("scaleT",
00456                         2,
00457                         Vec4f(1.0f/getTexSpacing(),
00458                               1.0f/getTexSpacing(),
00459                               1.0f/getTexSpacing(),
00460                               1.0f/getTexSpacing()));
00461    }
00462
00463    fp->addParameter("diffuseFactor",    6, Vec4f(1.0f, 1.0f, 1.0f, 1.0f));
00464    fp->addParameter("specularExponent", 7, Vec4f(6.0f, 6.0f, 6.0f, 6.0f));
00465    fp->addParameter("specularFactor",   8, Vec4f(0.2f, 0.2f, 0.2f, 0.2f));
00466    fp->addParameter("basecolor",        0, Vec4f(1.0f, 1.0f, 1.0f, 1.0f));
00467
00468    return fp;
00469 }
00470
00471 void QuadTreeTerrain::addMaterialChunks(void) const
00472 {
00473 #if 1
00474 #ifndef WITH_SINGLE_SHLCHUNK
00475 
00476    SimpleSHLVariableChunkUnrecPtr shlp = SimpleSHLVariableChunk::create();
00477
00478 //   shlp->setSHLChunk(s_shlChunk);
00479    shlp->addUniformVariable("texSampler",  0);
00480    shlp->addUniformVariable("nmapSampler", 1);
00481    // the following spares a second set of texture coordinates
00482    shlp->addUniformVariable("offsetS",    -getOriginTexX()/getTexSpacing());
00483    shlp->addUniformVariable("scaleS",     1.0f/getTexSpacing());
00484
00485    if(getTexYSpacing() != 1.0f)
00486    {
00487        shlp->addUniformVariable("offsetT", -getOriginTexY()/getTexYSpacing());
00488        shlp->addUniformVariable("scaleT",   1.0f/getTexYSpacing());
00489    }
00490    else
00491    {
00492        shlp->addUniformVariable("offsetT", -getOriginTexY()/getTexSpacing());
00493        shlp->addUniformVariable("scaleT",   1.0f/getTexSpacing());
00494    }
00495
00496    shlp->addUniformVariable("diffuseFactor",    1.0f);
00497    shlp->addUniformVariable("specularFactor",   0.2f);
00498    shlp->addUniformVariable("specularExponent", 6.0f);
00499    shlp->addUniformVariable("basecolor",        Vec3f(1.0, 1.0, 1.0));
00500
00501 #else
00502 
00503    SHLChunkUnrecPtr shl = SHLChunk::create();
00504
00505    shl->setVertexProgram  (_vp_program);
00506    shl->setFragmentProgram(_fp_program);
00507    shl->setUniformParameter("texSampler",  0);
00508    shl->setUniformParameter("nmapSampler", 1);
00509    // the following spares a second set of texture coordinates
00510
00511    shl->setUniformParameter("offsetS",    -getOriginTexX()/getTexSpacing());
00512    shl->setUniformParameter("scaleS",     1.0f/getTexSpacing());
00513
00514    if(getTexYSpacing() != 1.0f)
00515    {
00516        shl->setUniformParameter("offsetT", -getOriginTexY()/getTexYSpacing());
00517       shl->setUniformParameter("scaleT",    1.0f/getTexYSpacing());
00518    }
00519    else
00520    {
00521        shl->setUniformParameter("offsetT", -getOriginTexY()/getTexSpacing());
00522        shl->setUniformParameter("scaleT",   1.0f/getTexSpacing());
00523    }
00524
00525    shl->setUniformParameter("diffuseFactor",    1.0f);
00526    shl->setUniformParameter("specularFactor",   0.2f);
00527    shl->setUniformParameter("specularExponent", 6.0f);
00528    shl->setUniformParameter("basecolor",        Vec3f(1.0, 1.0, 1.0));
00529
00530 #endif
00531 
00532 #else
00533    VertexProgramChunkUnrecPtr   vp = createVPChunk();
00534    FragmentProgramChunkUnrecPtr fp = createFPChunk();
00535 #endif
00536 
00537    ImageUnrecPtr           normal_map_img = createNormalMap();
00538    TextureObjChunkUnrecPtr tex_normal_map = TextureObjChunk::create();
00539
00540    tex_normal_map->setImage(normal_map_img);
00541    tex_normal_map->setMinFilter(GL_LINEAR);
00542    tex_normal_map->setMagFilter(GL_LINEAR);
00543    tex_normal_map->setWrapS(GL_CLAMP);
00544    tex_normal_map->setWrapT(GL_CLAMP);
00545    //tex_normal_map->setEnvMode(GL_MODULATE);
00546
00547    ChunkMaterial *mat = dynamic_cast<ChunkMaterial *>(getMaterial());
00548
00549 #if 1
00550 
00551 #ifndef WITH_SINGLE_SHLCHUNK
00552    StateChunk *oldSHL  = mat->find(SimpleSHLChunk::getClassType());
00553
00554    if(oldSHL == NULL)
00555    {
00556        mat->addChunk(s_shlChunk);
00557    }
00558
00559    StateChunk *oldSHLP = mat->find(SimpleSHLVariableChunk::getClassType());
00560
00561    if(oldSHLP != NULL)
00562    {
00563        mat->subChunk(oldSHLP);
00564    }
00565
00566    mat->addChunk(shlp);
00567 #else
00568    StateChunk *oldSHL  = mat->find(SHLChunk::getClassType());
00569
00570    if(oldSHL != NULL)
00571    {
00572        mat->subChunk(oldSHL);
00573    }
00574
00575    mat->addChunk(shl);
00576 #endif
00577 
00578 #else
00579    StateChunk *oldVP  = mat->find(VertexProgramChunk::getClassType());
00580
00581    if(oldVP != NULL)
00582    {
00583        mat->subChunk(oldVP);
00584    }
00585
00586    mat->addChunk(vp);
00587
00588    StateChunk *oldFP  = mat->find(FragmentProgramChunk::getClassType());
00589    if(oldFP != NULL)
00590    {
00591        mat->subChunk(oldFP);
00592    }
00593
00594    mat->addChunk(fp);
00595 #endif
00596 
00597    StateChunk *oldTEX  = mat->find(TextureObjChunk::getClassType(), 1);
00598
00599    if(oldTEX != NULL)
00600    {
00601        mat->subChunk(oldTEX);
00602    }
00603
00604    mat->addChunk(tex_normal_map, 1);
00605 }
00606
00607 ImageTransitPtr QuadTreeTerrain::createNormalMap () const
00608 {
00609     ImageTransitPtr out = Image::create();
00610
00611     out->set(Image::OSG_RGB_PF, getWidth()-1, getWidth()-1, 1,
00612              1, 1, 0.0f, NULL, Image::OSG_UINT8_IMAGEDATA);
00613
00614     UInt8* outData = static_cast<UInt8 *>(out->editData());
00615
00616    GeoPnt3fProperty *pos = dynamic_cast<GeoPnt3fProperty *>(getPositions());
00617
00618    const Real32  maxData = 0.5f*TypeTraits<UInt8>::getMax();
00619
00620    UInt32 vnum, inum;
00621
00622    vnum = inum = 0;
00623
00624    for(Int32 z = 0; z < getWidth(); z++)
00625    {
00626        for (Int32 x = 0; x < getWidth(); x++, vnum++)
00627        {
00628            Vec3f tanx, tany, norm;
00629
00630            if(x == 0 || z == 0)
00631            {
00632                continue;
00633            }
00634
00635            //if (x > 0) {
00636            tanx = pos->getValue(vnum)          - pos->getValue(vnum-1);
00637            //} else {
00638            //tanx = pos->getValue(vnum+1)        - pos->getValue(vnum);     
00639            //}
00640            //if (z > 0) {
00641            tany = pos->getValue(vnum) - pos->getValue(vnum-getWidth());
00642            //} else {
00643            //tany = pos->getValue(vnum+getWidth()) - pos->getValue(vnum);
00644            //}
00645
00646            norm.setValue(tany.cross(tanx)); norm.normalize();
00647            outData[inum]   = UInt8(maxData*(norm[0]+1.0f));
00648            outData[inum+1] = UInt8(maxData*(norm[1]+1.0f));
00649            outData[inum+2] = UInt8(maxData*(norm[2]+1.0f));
00650            inum += 3;
00651        }
00652    }
00653
00654    return out;
00655 }
00656
00657
00658 void QuadTreeTerrain::calcD2ErrorMatrix()
00659 {
00660     //--- call recursive function for setting initial D2-Values ---
00661     calcD2ErrorMatrixRec(getWidth() / 2, getWidth() / 2, getWidth(), 1);
00662
00663     //--- ensure max level difference of 1 ------------------------
00664     propagateD2Errors();
00665 }
00666
00667 void QuadTreeTerrain::calcD2ErrorMatrixRec(Int32 centerX,
00668                                            Int32 centerZ,
00669                                            Int32 width,
00670                                            Int32 level)
00671 {
00672     if(level <= getLevel())
00673     {
00674         Int32 w2 = width / 2;
00675         Int32 w4 = width / 4;
00676         //--- current value ---------------------------------------------------
00677
00678         Int32 nodeIndex = centerZ * getWidth() + centerX;
00679
00680         if(level == getLevel()-getBorderDetail() &&
00681            ( centerX <= w2               ||
00682             (getWidth()-centerX) <= w2+1 ||
00683              centerZ <= w2               ||
00684             (getWidth()-centerZ) <= w2+1  )       )
00685         {
00686
00687             editHeightError(nodeIndex) =
00688                 (getBoundMax()[1]-getBoundMin()[1]) /
00689                 (Real32(width * getVertexSpacing()));
00690         }
00691         else if(level > getLevel()-getBorderDetail() &&
00692                 ( centerX <= w2               ||
00693                  (getWidth()-centerX) <= w2+1 ||
00694                   centerZ <= w2               ||
00695                  (getWidth()-centerZ) <= w2+1  )       )
00696         {
00697             editHeightError(nodeIndex) = 0.0f;
00698         }
00699         else
00700         {
00701             editHeightError(nodeIndex) = calcD2Value(centerX, centerZ, width);
00702         }
00703         //--- descend tree ------------------------------------------------
00704
00705         // nw child
00706         calcD2ErrorMatrixRec(centerX - w4, centerZ + w4, w2, level + 1);
00707         // ne child
00708         calcD2ErrorMatrixRec(centerX + w4, centerZ + w4, w2, level + 1);
00709         // se child
00710         calcD2ErrorMatrixRec(centerX + w4, centerZ - w4, w2, level + 1);
00711         // sw child
00712         calcD2ErrorMatrixRec(centerX - w4, centerZ - w4, w2, level + 1);
00713     }
00714 }
00715
00716 Real32 QuadTreeTerrain::calcD2Value (Int32 centerX,
00717                                      Int32 centerZ,
00718                                      Int32 width)
00719 {
00720     //--- init needed vars -----------------------------------------------
00721     Int32 rx = width / 2;                       // radius offset in x direction
00722     Int32 rz = rx * getWidth();                 // radius offset in z direction
00723
00724     Int32 c  = centerZ * getWidth() + centerX;  // center vertex
00725     Int32 n  = c - rz;                          // northern vertex
00726     Int32 w  = c - rx;                          // western
00727     Int32 s  = c + rz;                          // southern
00728     Int32 e  = c + rx;                          // eastern
00729     Int32 nw = n - rx;                          // ...
00730     Int32 sw = s - rx;
00731     Int32 se = s + rx;
00732     Int32 ne = n + rx;
00733
00734     // shortcut
00735     const GeoPnt3fProperty::StoredFieldType &v =
00736         dynamic_cast<GeoPnt3fProperty *>(getHeightVertices())->getField();
00737
00738     //--- north, east, south, west errors -------------------------------------
00739     Real32 nErr = osgAbs( v[n][1] -  (v[nw][1] + v[ne][1]) / 2.0f);
00740     Real32 eErr = osgAbs( v[e][1] -  (v[ne][1] + v[se][1]) / 2.0f);
00741     Real32 sErr = osgAbs( v[s][1] -  (v[se][1] + v[sw][1]) / 2.0f);
00742     Real32 wErr = osgAbs( v[w][1] -  (v[sw][1] + v[nw][1]) / 2.0f);
00743
00744
00745     //--- 1. and 2. diagonal error --------------------------------------------
00746
00747     Real32 d1Err = osgAbs( v[c][1] -  (v[nw][1] + v[se][1]) / 2.0f);
00748     Real32 d2Err = osgAbs( v[c][1] -  (v[ne][1] + v[sw][1]) / 2.0f);
00749
00750     //--- determine max of the 6 errors ---------------------------------------
00751
00752     Real32 maxErr = osgMax(osgMax(nErr, eErr),
00753                            osgMax(osgMax(sErr, wErr), osgMax(d1Err, d2Err)));
00754
00755     return (maxErr / (Real32(width * getVertexSpacing())));
00756 }
00757
00758
00759
00760 void QuadTreeTerrain::propagateD2Errors()
00761 {
00762     const Real32 D2K = MinGlobalRes / (2.0f * (MinGlobalRes - 1.0f));
00763
00764     // find the highest level
00765     //--- init vars -----------------------------------------------------------
00766
00767     Int32 steps = getWidth() / 2;
00768     Int32 width = 2;
00769
00770     Int32 centerX, centerZ;
00771
00772     Int32 w2;
00773     Int32 p1, p2, p3;                 // indices of parents
00774     Int32
00775         p1x = 0, p1z = 0,
00776         p2x = 0, p2z = 0,
00777         p3x = 0, p3z = 0;
00778
00779     MFReal32 *em = editMFHeightError(); // shortcut
00780
00781
00782     //--- iterate through all levels ------------------------------------------
00783     while(steps > 1)
00784     {
00785         w2 = width / 2;
00786         centerX = w2;
00787
00788         for(Int32 i = 0; i < steps; i++)
00789         {
00790             centerZ = w2;
00791             for(Int32 j = 0; j < steps; j++)
00792             {
00793                 //--- determine parents' indices ------------------------
00794                 switch ((centerX/width)%2 + 2*((centerZ/width)%2))
00795                 {
00796                     case 0:
00797                         p1x = centerX+w2;
00798                         p1z = centerZ+w2;
00799                         p2x = centerX-width-w2;
00800                         p2z = centerZ+w2;
00801                         p3x = centerX+w2;
00802                         p3z = centerZ-width-w2;
00803                         break;
00804                     case 1:
00805                         p1x = centerX-w2;
00806                         p1z = centerZ+w2;
00807                         p2x = centerX-w2;
00808                         p2z = centerZ-width-w2;
00809                         p3x = centerX+width+w2;
00810                         p3z = centerZ+w2;
00811                         break;
00812                     case 2:
00813                         p1x = centerX+w2;
00814                         p1z = centerZ-w2;
00815                         p2x = centerX+w2;
00816                         p2z = centerZ+width+w2;
00817                         p3x = centerX-width-w2;
00818                         p3z = centerZ-w2;
00819                         break;
00820                     case 3:
00821                         p1x = centerX-w2;
00822                         p1z = centerZ-w2;
00823                         p2x = centerX+width+w2;
00824                         p2z = centerZ-w2;
00825                         p3x = centerX-w2;
00826                         p3z = centerZ+width+w2;
00827                         break;
00828                 }
00829
00830                 //--- propagate to 3 parents -----------------------------
00831
00832                 Real32 d2K_EMthis = D2K * (*em)[centerZ * getWidth() + centerX];
00833
00834                 //--- to real father -------------------------------------
00835
00836                 p1     = p1z * getWidth() + p1x;
00837                 (*em)[p1] = osgMax((*em)[p1], d2K_EMthis);
00838
00839                 //--- other 2 "parents" ----------------------------------
00840
00841                 if(p2x >= 0         &&
00842                    p2x < getWidth() &&
00843                    p2z >= 0         &&
00844                    p2z < getWidth()    )
00845                 {
00846                     p2     = p2z * getWidth() + p2x;
00847                     (*em)[p2] = osgMax((*em)[p2], d2K_EMthis);
00848                 }
00849
00850                 if(p3x >= 0          &&
00851                    p3x < getWidth()  &&
00852                    p3z >= 0          &&
00853                    p3z < getWidth()    )
00854                 {
00855                     p3     = p3z * getWidth() + p3x;
00856                     (*em)[p3] = osgMax((*em)[p3], d2K_EMthis);
00857                 }
00858
00859                 centerZ += width;                  // advance zPos
00860             }
00861             centerX += width;                          // advance xPos
00862         }
00863         width *= 2;
00864         steps /= 2;
00865     }
00866 }
00867
00868 Real32 QuadTreeTerrain::calcSubDiv(Int32 nodeIndex, Int32 width)
00869 {
00870    //--- calculate the eye distance to each node, using L1-Norm --------------
00871
00872    const Pnt3f& v = getVertex(nodeIndex);
00873
00874    Real32 eyeDist =
00875        osgAbs(v[0] - getEyePoint()[0]) +
00876        osgAbs(getEyeHeight()) +
00877        osgAbs(v[2] - getEyePoint()[2]);
00878
00879    //--- calc subdiv value ---------------------------------------------------
00880    return
00881        eyeDist / ((width * getVertexSpacing()) * MinGlobalRes *
00882                   osgMax(getDetail() * getHeightError(nodeIndex), 1.0f));
00883 }
00884
00885 void QuadTreeTerrain::triangulateMeshRec(const FrustumVolume &frustum,
00886                                                Int32          c,
00887                                                Int32          width,
00888                                                Int32          level)
00889 {
00890     if(level > getLevel())
00891     {
00892         return;
00893     }
00894
00895     //--- check view frustum -------------------------------------------------      
00896 #if 1
00897     if(level <= getLevel() - 2)
00898     {
00899         Pnt3f point;
00900         getVertex(c, point);
00901
00902         SphereVolume current(point,
00903                              (0.5f*width) * getVertexSpacing() * 15.0f);
00904
00905         if(!OSG::intersect(frustum, current))
00906         {
00907             return;
00908         }
00909     }
00910 #endif
00911 
00912     //--- go on ---------------------------------------------------------------
00913
00914     Real32 subDiv = calcSubDiv(c, width);
00915
00916     Int32   w2     = width / 2;
00917     Int32   w4     = width / 4;
00918     Int32   r      = w4 * getWidth();
00919
00920     Int32   nw     = c - r - w4;
00921     Int32   ne     = c - r + w4;
00922     Int32   sw     = c + r - w4;
00923     Int32   se     = c + r + w4;
00924
00925     if(subDiv >= 1.0f)
00926     { //--- NOT to be drawn --------------------
00927         deleteNode(c, width);
00928         return;
00929     }
00930     else
00931     { //--- leaf or parent? ---------------------
00932         editHeightQuad(c) = calcBlend(subDiv);
00933
00934         triangulateMeshRec(frustum, nw, w2, level+1);
00935         triangulateMeshRec(frustum, ne, w2, level+1);
00936         triangulateMeshRec(frustum, sw, w2, level+1);
00937         triangulateMeshRec(frustum, se, w2, level+1);
00938
00939         return;
00940     }
00941 }
00942
00943 //=== Render Terrain-Mesh ===================================================
00944 bool QuadTreeTerrain::renderMeshRec(const FrustumVolume &frustum,
00945                                           Int32          x,
00946                                           Int32          z,
00947                                           Int32          width,
00948                                           Int32          level,
00949                                           Int32          dirToFather,
00950                                           Real32         rhNW,
00951                                           Real32         rhNE,
00952                                           Real32         rhSW,
00953                                           Real32         rhSE       )
00954 {
00955    // returns true if caller has to draw this corner
00956     Int32 c = z * getWidth() + x;              // center
00957
00958     if(getHeightQuad(c) == TypeTraits<Real32>::getMax())
00959     { // stop recursion
00960         return true;
00961     }
00962
00963     Int32 w2 = width / 2;
00964     Int32 w4 = width / 4;
00965
00966
00967     //--- check view frustum --------------------------------------------------
00968
00969 #if 1
00970     if(level <= getLevel()-2)
00971     {
00972         Pnt3f point;
00973         getVertex(c, point);
00974
00975         SphereVolume current;
00976         current.setCenter(point);
00977         current.setRadius(Real32(w2) * Real32(getVertexSpacing()) * 5.0f);
00978
00979         if(!OSG::intersect(frustum, current))
00980         {
00981             return false;
00982         }
00983     }
00984 #endif
00985 
00986     //--- init necessary vars -------------------------------------------------
00987
00988     Int32 rx = width / 2;             // radius offset in x direction
00989     Int32 rz = rx * getWidth();            // radius offset in z direction
00990
00991     Int32 n  = c - rz;                // northern vertex
00992     Int32 w  = c - rx;                // western
00993     Int32 s  = c + rz;                // southern
00994     Int32 e  = c + rx;                // eastern
00995
00996 #if 0
00997     Int32 nw = n - rx;
00998     Int32 sw = s - rx;
00999     Int32 se = s + rx;
01000     Int32 ne = n + rx;
01001 #endif
01002 
01003     //--- get all 9 heights ---------------------------------------------------
01004
01005     Real32 blend = getHeightQuad(c);
01006
01007     Real32 hC  = getHeight(c,
01008                            width,
01009                            dirToFather,
01010                            C,
01011                            blend,
01012                            rhNW,
01013                            rhNE,
01014                            rhSW,
01015                            rhSE);
01016
01017     Real32 hN  = getHeight(n,
01018                            width,
01019                            dirToFather,
01020                            N,
01021                            blend,
01022                            rhNW,
01023                            rhNE,
01024                            rhSW,
01025                            rhSE);
01026
01027     Real32 hS  = getHeight(s,
01028                            width,
01029                            dirToFather,
01030                            S,
01031                            blend,
01032                            rhNW,
01033                            rhNE,
01034                            rhSW,
01035                            rhSE);
01036
01037     Real32 hW  = getHeight(w,
01038                            width,
01039                            dirToFather,
01040                            W,
01041                            blend,
01042                            rhNW,
01043                            rhNE,
01044                            rhSW,
01045                            rhSE);
01046     Real32 hE  = getHeight(e,
01047                            width,
01048                            dirToFather,
01049                            E,
01050                            blend,
01051                            rhNW,
01052                            rhNE,
01053                            rhSW,
01054                            rhSE);
01055
01056     //--- check if node has children ------------------------------------------
01057
01058     bool corners[] = { true, true, true, true, true, true, true, true };
01059     bool isLeaf  = false;
01060
01061     if(level < getLevel())
01062     {
01063         corners[NW] = renderMeshRec(frustum,
01064                                     x-w4,
01065                                     z-w4,
01066                                     w2,
01067                                     level+1,
01068                                     SE,
01069                                     rhNW,
01070                                     hN,
01071                                     hW,
01072                                     hC );
01073
01074         corners[NE] = renderMeshRec(frustum,
01075                                     x+w4,
01076                                     z-w4,
01077                                     w2,
01078                                     level+1,
01079                                     SW,
01080                                     hN,
01081                                     rhNE,
01082                                     hC,
01083                                     hE );
01084
01085         corners[SW] = renderMeshRec(frustum,
01086                                     x-w4,
01087                                     z+w4,
01088                                     w2,
01089                                     level+1,
01090                                     NE,
01091                                     hW,
01092                                     hC,
01093                                     rhSW,
01094                                     hS );
01095
01096         corners[SE] = renderMeshRec(frustum,
01097                                     x+w4,
01098                                     z+w4,
01099                                     w2,
01100                                     level+1,
01101                                     NW,
01102                                     hC,
01103                                     hE,
01104                                     hS,
01105                                     rhSE);
01106     }
01107     else
01108     {
01109         isLeaf = true;
01110     }
01111
01112     if(corners[NW] || corners[SW] || corners[SE] || corners[NE])
01113     {
01114         if (corners[NW] && corners[SW] && corners[SE] && corners[NE])
01115         {
01116             isLeaf = true;
01117         }
01118
01119         //--- determine 'corners' to be drawn --------------------------------
01120
01121         const MFReal32 &qm = *getMFHeightQuad(); // shortcut
01122
01123         // check array bounds and whether the neighbor exists
01124         if ((z-width >= 0    ) &&
01125             (qm[c-(getWidth()*width)] == TypeTraits<Real32>::getMax()))
01126         {
01127             corners[N] = false;
01128         }
01129         if ((x+width <  getWidth()) &&
01130             (qm[c+width]         == TypeTraits<Real32>::getMax()))
01131         {
01132             corners[E] = false;
01133         }
01134         if ((z+width <  getWidth()) &&
01135             (qm[c+(getWidth()*width)] == TypeTraits<Real32>::getMax()))
01136         {
01137             corners[S] = false;
01138         }
01139         if ((x-width >= 0    ) &&
01140             (qm[c-width]         == TypeTraits<Real32>::getMax())) {
01141             corners[W] = false;
01142         }
01143
01144         createFanAround(x,
01145                         z,
01146                         width,
01147                         corners,
01148                         isLeaf,
01149                         hC,
01150                         hN,
01151                         hS,
01152                         hW,
01153                         hE,
01154                         rhNW,
01155                         rhNE,
01156                         rhSW,
01157                         rhSE);
01158     }
01159     return false;
01160 }
01161
01162 void QuadTreeTerrain::createFanAround (Int32 x,
01163                                        Int32 z,
01164                                        Int32 width,
01165                                        bool* corners,
01166                                        bool isLeaf,
01167                                        Real32 hC,
01168                                        Real32 hN,
01169                                        Real32 hS,
01170                                        Real32 hW,
01171                                        Real32 hE,
01172                                        Real32 hNW,
01173                                        Real32 hNE,
01174                                        Real32 hSW,
01175                                        Real32 hSE)
01176 {
01177    //--- init necessary vars -------------------------------------------------
01178
01179     Int32 rx = width / 2;             // radius offset in x direction
01180     Int32 rz = rx * getWidth();            // radius offset in z direction
01181
01182     Int32 c  = z * getWidth() + x;         // center vertex
01183     Int32 n  = c - rz;                // northern vertex
01184     Int32 w  = c - rx;                // western
01185     Int32 s  = c + rz;                // southern
01186     Int32 e  = c + rx;                // eastern
01187     Int32 nw = n - rx;
01188     Int32 sw = s - rx;
01189     Int32 se = s + rx;
01190     Int32 ne = n + rx;
01191
01192     //--- pre-check corners array ---------------------------------------------
01193     if(!isLeaf)
01194     {
01195         if (!corners[NW] && !corners[SW]) {  corners[W] = false;  }
01196         if (!corners[SW] && !corners[SE]) {  corners[S] = false;  }
01197         if (!corners[SE] && !corners[NE]) {  corners[E] = false;  }
01198         if (!corners[NE] && !corners[NW]) {  corners[N] = false;  }
01199     }
01200
01201 #ifdef WITH_TRIANGLES
01202     // adapt height
01203
01204     GeoPnt3fProperty::StoredFieldType &pos =
01205         dynamic_cast<GeoPnt3fPropertyPtr>(getPositions())->editField();
01206
01207     pos[c][1]  = hC;
01208     //--- check western quarter -----------------------------------------------
01209
01210     GeoUInt32Property::StoredFieldType &ind =
01211         dynamic_cast<GeoUInt32PropertyPtr>(getIndices())->editField();
01212
01213     if(corners[NW] || corners[W] || corners[SW])
01214     {
01215         pos[nw][1] = hNW;
01216         pos[w][1]  = hW;
01217         pos[sw][1] = hSW;
01218
01219         if(corners[NW])
01220         {
01221             ind.push_back(nw);
01222             if(corners[W])
01223             {
01224                 ind.push_back(w);
01225                 if (corners[SW])
01226                 {
01227                     ind.push_back(c);
01228                     ind.push_back(w);
01229                     ind.push_back(sw);
01230                 }
01231             }
01232             else
01233             {
01234                 if(corners[SW])
01235                 {
01236                     ind.push_back(sw);
01237                 }
01238                 else
01239                 {
01240                     ind.push_back(w);
01241                 }
01242             }
01243             ind.push_back(c);
01244         }
01245         else if(corners[W] || corners[SW])
01246         {
01247             ind.push_back(w);
01248             ind.push_back(sw);
01249             ind.push_back(c);
01250         }
01251     }
01252
01253     //--- check southern quarter ----------------------------------------------
01254
01255     if(corners[SW] || corners[S] || corners[SE])
01256     {
01257         pos[sw][1] = hSW;
01258         pos[s][1]  = hS;
01259         pos[se][1] = hSE;
01260
01261         if(corners[SW])
01262         {
01263             ind.push_back(sw);
01264
01265             if(corners[S])
01266             {
01267                 ind.push_back(s);
01268
01269                 if(corners[SE])
01270                 {
01271                     ind.push_back(c);
01272                     ind.push_back(s);
01273                     ind.push_back(se);
01274                 }
01275             }
01276             else
01277             {
01278                 if(corners[SE])
01279                 {
01280                     ind.push_back(se);
01281                 }
01282                 else
01283                 {
01284                     ind.push_back(s);
01285                 }
01286             }
01287             ind.push_back(c);
01288         }
01289         else if (corners[S] || corners[SE])
01290         {
01291             ind.push_back(s);
01292             ind.push_back(se);
01293             ind.push_back(c);
01294         }
01295     }
01296
01297     //--- check eastern quarter -----------------------------------------------
01298
01299     if(corners[SE] || corners[E] || corners[NE])
01300     {
01301         pos[se][1] = hSE;
01302         pos[e][1]  = hE;
01303         pos[ne][1] = hNE;
01304
01305         if(corners[SE])
01306         {
01307             ind.push_back(se);
01308             if(corners[E])
01309             {
01310                 ind.push_back(e);
01311                 if(corners[NE])
01312                 {
01313                     ind.push_back(c);
01314                     ind.push_back(e);
01315                     ind.push_back(ne);
01316                 }
01317             }
01318             else
01319             {
01320                 if(corners[NE])
01321                 {
01322                     ind.push_back(ne);
01323                 }
01324                 else
01325                 {
01326                     ind.push_back(e);
01327                 }
01328             }
01329             ind.push_back(c);
01330         }
01331         else if(corners[E] || corners[NE])
01332         {
01333             ind.push_back(e);
01334             ind.push_back(ne);
01335             ind.push_back(c);
01336         }
01337     }
01338
01339     //--- check northern quarter ----------------------------------------------
01340     if(corners[NE] || corners[N] || corners[NW])
01341     {
01342         pos[ne][1] = hNE;
01343         pos[e][1]  = hN;
01344         pos[nw][1] = hNW;
01345         if(corners[NE])
01346         {
01347             ind.push_back(ne);
01348             if(corners[N])
01349             {
01350                 ind.push_back(n);
01351                 if (corners[NW])
01352                 {
01353                     ind.push_back(c);
01354                     ind.push_back(n);
01355                     ind.push_back(nw);
01356                 }
01357             }
01358             else
01359             {
01360                 if(corners[NW])
01361                 {
01362                     ind.push_back(nw);
01363                 }
01364                 else
01365                 {
01366                     ind.push_back(n);
01367                 }
01368             }
01369             ind.push_back(c);
01370         }
01371         else if(corners[N] || corners[NW])
01372         {
01373             ind.push_back(n);
01374             ind.push_back(nw);
01375             ind.push_back(c);
01376         }
01377     }
01378 #else // WITH_TRIANGLE_FANS
01379 
01380     GeoUInt32Property::StoredFieldType &ind =
01381         dynamic_cast<GeoUInt32Property *>(getIndices())->editField();
01382
01383     UInt32 prev = ind.size();
01384
01385     GeoPnt3fProperty::StoredFieldType &pos =
01386         dynamic_cast<GeoPnt3fProperty *>(getPositions())->editField();
01387
01388     pos[c][1] = hC;
01389     ind.push_back(c);
01390     UInt32 last  = C;
01391     UInt32 first = C;
01392
01393     if(corners[NW])
01394     {
01395         SINFO << " NW" << std::flush;
01396         pos[nw][1] = hNW;
01397         ind.push_back(nw);
01398         last = NW;
01399         if(first == C)
01400         {
01401             first = NW;
01402         }
01403     }
01404     if(corners[W])
01405     {
01406         SINFO << " W" << std::flush;
01407         pos[w][1] = hW;
01408         ind.push_back(w);
01409         last = W;
01410         if(first == C)
01411         {
01412             first = W;
01413         }
01414     }
01415     if(corners[SW])
01416     {
01417         SINFO << " SW" << std::flush;
01418         pos[sw][1] = hSW;
01419         ind.push_back(sw);
01420         last = SW;
01421         if(first == C)
01422         {
01423             first = SW;
01424         }
01425     }
01426     if(corners[S])
01427     {
01428         SINFO << " S" << std::flush;
01429         if(last == W)
01430         {
01431             ind.push_back(c);
01432         }
01433         pos[s][1] = hS;
01434         ind.push_back(s);
01435         last = S;
01436         if(first == C)
01437         {
01438             first = S;
01439         }
01440     }
01441     if(corners[SE])
01442     {
01443         SINFO << " SE" << std::flush;
01444         pos[se][1] = hSE;
01445         ind.push_back(se);
01446         last = SE;
01447         if(first == C)
01448         {
01449             first = SE;
01450         }
01451     }
01452     if(corners[E])
01453     {
01454         SINFO << " E" << std::flush;
01455         if(last == S || last == W)
01456         {
01457             ind.push_back(c);
01458         }
01459         pos[e][1] = hE;
01460         ind.push_back(e);
01461         last = E;
01462         if(first == C)
01463         {
01464             first = E;
01465         }
01466     }
01467     if(corners[NE])
01468     {
01469         SINFO << " NE" << std::flush;
01470         pos[ne][1] = hNE;
01471         ind.push_back(ne);
01472         last = NE;
01473         if(first == C)
01474         {
01475             first = NE;
01476         }
01477     }
01478     if(corners[N])
01479     {
01480         SINFO << " N" << std::flush;
01481         if(last == E || last == S || last == W)
01482         {
01483             ind.push_back(c);
01484         }
01485         pos[n][1] = hN;
01486         ind.push_back(n);
01487         last = N;
01488         if(first == C)
01489         {
01490             first = N;
01491         }
01492     }
01493
01494     // if first and last point are not midpoints, then close triangle-fan
01495     if(!(first%2 == 1 && last%2 == 1))
01496     { // first and last point are not midpoints
01497         ind.push_back(ind[prev+1]);
01498     }
01499
01500     // triangle fan with length (ind.size()-prev)
01501     GeoUInt32Property *len =
01502         dynamic_cast<GeoUInt32Property *>(getLengths());
01503
01504     len->push_back((ind.size()-prev));
01505
01506     GeoUInt8Property *typ = dynamic_cast<GeoUInt8Property *>(getTypes());
01507
01508     typ->push_back(GL_TRIANGLE_FAN);
01509     SINFO << "added triangle-fan of length " << ind.size()-prev << std::endl;
01510
01511 #endif
01512 }
01513
01514 void QuadTreeTerrain::setInterleaved(Int32 index, Real32 height)
01515 {
01516    // add position/tex-coord
01517     GeoUInt32Property::StoredFieldType &ind =
01518         dynamic_cast<GeoUInt32Property *>(getIndices())->editField();
01519
01520     ind.push_back(index);
01521 }
01522
01523
01524 Real32 QuadTreeTerrain::getHeight(Int32 index,
01525                                   Int32 width,
01526                                   Int32 dirToFather,
01527                                   Int32 neswc,
01528                                   Real32 blend,
01529                                   Real32 rhNW,
01530                                   Real32 rhNE,
01531                                   Real32 rhSW,
01532                                   Real32 rhSE)
01533 {
01534     const GeoPnt3fProperty::StoredFieldType &vertices =
01535         dynamic_cast<GeoPnt3fProperty *>(getHeightVertices())->getField();
01536
01537     //--- init vars ------------------------------------------------------------
01538     Real32 height = vertices[index][1];
01539
01540     //--- determine blend values if this is a leaf ----------------------------
01541
01542     if(getGeoMorphing())
01543     {
01544         //--- init other vars -------------------------------------------------
01545
01546         Int32 rx = width / 2;             // radius offset in x dir.
01547         Int32 rz = rx * getWidth();            // radius offset in z dir.
01548
01549         Int32 z  = index / getWidth();         // z coord. of index
01550         Int32 x  = index - (z * getWidth());   // x coord. of index
01551
01552
01553         //--- determine offset and center indices -----------------------------
01554
01555         switch(neswc)
01556         {
01557             case C:
01558                 switch (dirToFather)
01559                 {
01560                     case SE:
01561                     case NW:
01562                         height =
01563                             (1.0f-blend) * (rhNW + rhSE)/2.0f + blend*height;
01564                         break;
01565                     case NE:
01566                     case SW:
01567                         height =
01568                             (1.0f-blend) * (rhNE+rhSW)/2.0f + blend*height;
01569                         break;
01570                 }
01571                 break;
01572
01573             case N:
01574                 if(z - rx >= 0)
01575                 {
01576                     blend = osgMin(blend,getHeightQuad(index - rz));
01577                 }
01578
01579                 height = (1.0f-blend) * (rhNW+rhNE)/2.0f + blend*height;
01580
01581                 break;
01582
01583             case S:
01584                 if(z + rx < getWidth())
01585                 {
01586                     blend=osgMin(blend,getHeightQuad(index + rz));
01587                 }
01588
01589                 height = (1.0f-blend) * (rhSW+rhSE)/2.0f + blend*height;
01590
01591                 break;
01592
01593             case W:
01594                 if(x - rx >= 0)
01595                 {
01596                     blend=osgMin(blend,getHeightQuad(index - rx));
01597                 }
01598
01599                 height = (1.0f-blend) * (rhNW+rhSW)/2.0f + blend*height;
01600
01601                 break;
01602
01603             case E:
01604                 if(x + rx < getWidth())
01605                 {
01606                     blend=osgMin(blend,getHeightQuad(index + rx));
01607                 }
01608
01609                 height = (1.0f-blend) * (rhNE+rhSE)/2.0f + blend*height;
01610
01611                 break;
01612         }
01613     }
01614
01615     return height;
01616 }
01617
01618 Real32 QuadTreeTerrain::calcBlend(Real32 subDiv)
01619 {
01620    //--- calc blend factor ----------------------
01621    Real32 blend = 2 * (1.0f - subDiv);
01622    blend = osgMin(blend, 1.0f);
01623    return blend;
01624 }
01625
01626 void QuadTreeTerrain::deleteNode (Int32 index, Int32 width)
01627 {
01628     //--- init vars ---------------------------------------
01629     width /= 2;
01630
01631     Int32 rx = width / 2;
01632     Int32 rz = rx * getWidth();
01633
01634     //--- delete this node and descend --------------------
01635
01636     editHeightQuad(index) = TypeTraits<Real32>::getMax();    // C (this)
01637
01638     if(width > 2)
01639     {
01640         deleteNode(index - rz - rx, width);    // NW
01641         deleteNode(index - rz + rx, width);    // NE
01642         deleteNode(index + rz - rx, width);    // SW
01643         deleteNode(index + rz + rx, width);    // SE
01644     }
01645 }
01646
01647 Real32 QuadTreeTerrain::getHeightAboveGround (const Pnt3f& eye)
01648 {
01649     Real32 ex = eye[0];
01650     Real32 ey = eye[1];
01651     Real32 ez = eye[2];
01652     //--- init needed vars --------------------------------------------------
01653     Real32 ulx = getVertex(0)[0];                    // upper left x coord.
01654     Real32 ulz = getVertex(0)[2];                    // upper left z coord.
01655
01656     Real32 lrx = getVertex(getNumVertices())[0];      // lower right...
01657     Real32 lrz = getVertex(getNumVertices())[2];      // ...
01658
01659     //--- determine height above ground -------------------------------------
01660     if      (ex < ulx) {  ex = ulx;  }
01661     else if (ex > lrx) {  ex = lrx;  }
01662
01663     if      (ez < ulz) {  ez = ulz;  }
01664     else if (ez > lrz) {  ez = lrz;  }
01665
01666     Int32 x = Int32((ex - ulx) / getVertexSpacing());
01667     Int32 z = Int32((ez - ulz) / getVertexSpacing());
01668
01669     if (x > getWidth() - 1) {  x = getWidth() - 1;  }
01670     if (z > getWidth() - 1) {  z = getWidth() - 1;  }
01671
01672     Real32 ground = getVertex((z * getWidth()) + x)[1];
01673
01674     return ey - ground;
01675 }
01676
01677 #ifdef OSG_OLD_RENDER_ACTION
01678 Action::ResultE QuadTreeTerrain::renderEnter (Action* action)
01679 {
01680     RenderAction* da = dynamic_cast<RenderAction*>(action);
01681
01682     if(da != NULL && getWidth() > 0)
01683     { // dynamic tesselation
01684         Time startTime = getSystemTime();
01685         //--- create Terrain Mesh ---------------------------------------------
01686
01687         GeoUInt32PropertyPtr len =
01688             dynamic_cast<GeoUInt32PropertyPtr>(getLengths());
01689
01690         if(getUpdateTerrain() || len->size() == 0)
01691         {
01692             if(!getEyePointValid())
01693             {
01694                 Matrix camera  = da->getCameraToWorld();
01695                 Matrix toworld = da->top_matrix();
01696                 //action->getActNode()->getToWorld(toworld);
01697                 toworld.invert();
01698                 camera.multLeft(toworld);
01699                 //--- triangulate Mesh ----------------------------------------
01700                 setEyePoint(Pnt3f(camera[3][0], camera[3][1], camera[3][2]));
01701             }
01702
01703             setEyeHeight(getHeightAboveGround(getEyePoint()));
01704
01705             const FrustumVolume& frustum = da->getFrustum();
01706
01707             triangulateMeshRec(frustum,
01708                                getWidth()*getWidth()/2,
01709                                getWidth()-1,
01710                                1);
01711
01712             GeoUInt8PropertyPtr  typ =
01713                 dynamic_cast<GeoUInt8PropertyPtr >(getTypes());
01714
01715             GeoUInt32PropertyPtr ind =
01716                 dynamic_cast<GeoUInt32PropertyPtr>(getIndices());
01717
01718             len->clear();
01719             typ->clear();
01720             ind->clear();
01721
01722             const GeoPnt3fProperty::StoredFieldType &v =
01723                 dynamic_cast<GeoPnt3fPropertyPtr>(
01724                     getHeightVertices())->getField();
01725
01726             Real32 hNW = v[0][1];
01727             Real32 hNE = v[(getWidth()-1)][1];
01728             Real32 hSW = v[(getWidth()-1)*getWidth()][1];
01729             Real32 hSE = v[((getWidth()-1)*getWidth()+(getWidth()-1))][1];
01730             renderMeshRec(frustum,
01731                           getWidth()/2,
01732                           getWidth()/2,
01733                           getWidth()-1,
01734                           1,
01735                           C,
01736                           hNW,
01737                           hNE,
01738                           hSW,
01739                           hSE);
01740 #ifdef WITH_TRIANGLES
01741             len->push_back((ind->size()));
01742             typ->push_back(GL_TRIANGLES);
01743 #endif
01744 
01745         }
01746         Time endTime = getSystemTime();
01747
01748 //        SNOTICE << "terrain time:     " 
01749 //                << endTime-startTime 
01750 //                << " ms" 
01751 //                << std::endl;
01752     }
01753
01754     return Inherited::renderActionEnterHandler(action);
01755 }
01756 #endif
01757 
01758 Action::ResultE QuadTreeTerrain::renderEnter (Action* action)
01759 {
01760     RenderAction* da =
01761         dynamic_cast<RenderAction*>(action);
01762
01763
01764     this->doRenderEnter(da->getFrustum(),
01765                         da->getActivePartition()->getCameraToWorld(),
01766                         da->getActivePartition()->topMatrix());
01767
01768
01769     return Inherited::renderActionEnterHandler(action);
01770 }
01771
01772 Action::ResultE QuadTreeTerrain::doRenderEnter (const FrustumVolume &frustum,
01773                                                 Matrix         camera,
01774                                                 Matrix         toworld)
01775 {
01776     if(getWidth() > 0)
01777     { // dynamic tesselation
01778         // Time startTime = getSystemTime();
01779         //--- create Terrain Mesh ---------------------------------------------
01780
01781         GeoUInt32Property *len =
01782             dynamic_cast<GeoUInt32Property *>(getLengths());
01783
01784         if(getUpdateTerrain() || len->size() == 0)
01785         {
01786             if(!getEyePointValid())
01787             {
01788 //                Matrix camera  = da->getCameraToWorld();
01789 //                Matrix toworld = da->top_matrix();
01790                 //action->getActNode()->getToWorld(toworld);
01791                 toworld.invert();
01792                 camera.multLeft(toworld);
01793                 //--- triangulate Mesh ----------------------------------------
01794                 setEyePoint(Pnt3f(camera[3][0], camera[3][1], camera[3][2]));
01795             }
01796
01797             setEyeHeight(getHeightAboveGround(getEyePoint()));
01798
01799 //            const FrustumVolume& frustum = da->getFrustum();
01800
01801             triangulateMeshRec(frustum,
01802                                getWidth()*getWidth()/2,
01803                                getWidth()-1,
01804                                1);
01805
01806             GeoUInt8Property *typ =
01807                 dynamic_cast<GeoUInt8Property  *>(getTypes());
01808
01809             GeoUInt32Property *ind =
01810                 dynamic_cast<GeoUInt32Property *>(getIndices());
01811
01812             len->clear();
01813             typ->clear();
01814             ind->clear();
01815
01816             const GeoPnt3fProperty::StoredFieldType &v =
01817                 dynamic_cast<GeoPnt3fProperty *>(
01818                     getHeightVertices())->getField();
01819
01820             Real32 hNW = v[0][1];
01821             Real32 hNE = v[(getWidth()-1)][1];
01822             Real32 hSW = v[(getWidth()-1)*getWidth()][1];
01823             Real32 hSE = v[((getWidth()-1)*getWidth()+(getWidth()-1))][1];
01824             renderMeshRec(frustum,
01825                           getWidth()/2,
01826                           getWidth()/2,
01827                           getWidth()-1,
01828                           1,
01829                           C,
01830                           hNW,
01831                           hNE,
01832                           hSW,
01833                           hSE);
01834 #ifdef WITH_TRIANGLES
01835             len->push_back((ind->size()));
01836             typ->push_back(GL_TRIANGLES);
01837 #endif
01838 
01839         }
01840         // Time endTime = getSystemTime();
01841
01842         // SNOTICE << "terrain time:     "
01843         //         << endTime-startTime
01844         //         << " ms"
01845         //         << std::endl;
01846     }
01847
01848     return Action::Continue;
01849 }
01850
01851 void QuadTreeTerrain::adjustVolume (Volume& volume)
01852 {
01853     volume.setValid (false);
01854     //volume.setStatic(true);
01855     volume.setEmpty();
01856     volume.extendBy(getBoundMin());
01857     volume.extendBy(getBoundMax());
01858     volume.setValid();
01859     Pnt3f minB, maxB;
01860     volume.getBounds(minB, maxB);
01861 }
01862
01863 /*----------------------------- class specific ----------------------------*/
01864
01865 void QuadTreeTerrain::changed(ConstFieldMaskArg whichField,
01866                               UInt32            origin,
01867                               BitVector         details)
01868 {
01869     // create single SHL chunk, parameters are changed in separate SHL 
01870     // parameter chunks
01871     if(s_shlChunk == NULL)
01872     {
01873         s_shlChunk = createSHLChunk();
01874     }
01875
01876     // changed HeightData
01877     // * update HeightError and HeightQuad
01878     if((whichField & HeightDataFieldMask) && getHeightData() != NULL)
01879     {
01880         if(getHeightData()->getPixelFormat() != Image::OSG_L_PF)
01881         {
01882             SLOG << "heightData is not of LUMINANCE format! " << std::endl;
01883             getHeightData()->reformat(Image::OSG_L_PF);
01884         }
01885         {
01886             setWidth(getHeightData()->getWidth());
01887             assert(getHeightData()->getHeight() == getWidth());
01888             setLevel(UInt32(osgLog ((getWidth() - 1.0f)) / osgLog(2.0f)));
01889
01890             SLOG << "found data width="
01891                  << getWidth()
01892                  << ", level="
01893                  << getLevel()
01894                  << std::endl;
01895
01896             // clear quad array
01897             editMFHeightError()->resize(getWidth()*getWidth());
01898             editMFHeightQuad() ->resize(getWidth()*getWidth());
01899
01900             for(Int32 i=0; i<getWidth(); ++i)
01901             {
01902                 for(Int32 j=0; j<getWidth(); ++j)
01903                 {
01904                     editHeightQuad(j*getWidth() + i) =
01905                         TypeTraits<Real32>::getMax();
01906                 }
01907             }
01908
01909             //--- fill Vertices and TexCoords arrays with values -------------
01910             Int32  vnum = 0;                                 // vertex number
01911
01912             Int32 x, z;
01913
01914             GeoPnt3fPropertyUnrecPtr  pos = GeoPnt3fProperty ::create();
01915             GeoUInt32PropertyUnrecPtr ind = GeoUInt32Property::create();
01916             GeoUInt8PropertyUnrecPtr  typ = GeoUInt8Property ::create();
01917             GeoUInt32PropertyUnrecPtr len = GeoUInt32Property::create();
01918
01919             Pnt3f boundMin(Inf, Inf, Inf);
01920             Pnt3f boundMax(NegInf, NegInf, NegInf);
01921
01922             pos->editFieldPtr()->clear();
01923
01924             for(z = 0; z < getWidth(); z++)
01925             {
01926                 for (x = 0; x < getWidth(); x++, vnum++)
01927                 {
01928                     Real32 px = (x * getVertexSpacing() + getOriginX());
01929                     Real32 py = (getHeightDataScaled(vnum) * getHeightScale());
01930                     Real32 pz = (z * getVertexSpacing() + getOriginY());
01931
01932                     pos ->push_back(Pnt3f(px, py, pz));
01933                     boundMin[0] = osgMin(boundMin[0], px);
01934                     boundMin[1] = osgMin(boundMin[1], py);
01935                     boundMin[2] = osgMin(boundMin[2], pz);
01936                     boundMax[0] = osgMax(boundMax[0], px);
01937                     boundMax[1] = osgMax(boundMax[1], py);
01938                     boundMax[2] = osgMax(boundMax[2], pz);
01939                 }
01940             }
01941
01942
01943      // original heightfield points; Positions is used for geomorphing
01944
01945             GeoPnt3fPropertyUnrecPtr vert =
01946                 dynamic_pointer_cast<GeoPnt3fProperty>(pos->clone());
01947
01948             // geometry fields
01949             setPositions(pos);
01950             setTypes(typ);
01951             setIndices(ind);
01952             setLengths(len);
01953             setDlistCache(false); // no dlists for changing geometry
01954             // terrain fields
01955             setHeightVertices(vert);
01956             setBoundMin(boundMin);
01957             setBoundMax(boundMax);
01958
01959             // calc D2 errors for quality control
01960             calcD2ErrorMatrix();
01961         }
01962     }
01963
01964     if(((whichField & MaterialFieldMask) ||
01965         (whichField & PerPixelLightingFieldMask)) && getPositions() != NULL)
01966     {
01967         // create texCoords 
01968         // Width steps between [0..1.0] for:
01969         Real32 sstep = getTexSpacing() / Real32(getWidth());
01970
01971         Real32 tstep = sstep;
01972
01973         if(getTexYSpacing() != 1.0f)
01974         {
01975             // it is sufficient to just set field TexSpacing for 
01976             // isotropic scaling
01977             tstep = getTexYSpacing() / Real32(getWidth());
01978        }
01979
01980         GeoVec2fPropertyUnrecPtr tex;
01981
01982         if(getTexCoords() == NULL)
01983         {
01984             tex = GeoVec2fProperty::create();
01985         }
01986         else
01987         {
01988             tex = dynamic_cast<GeoVec2fProperty *>(getTexCoords());
01989         }
01990
01991         UInt32 vnum = 0;
01992         Int32 x, z;
01993         Real32 s, t;                                     // tex coords
01994
01995         tex->editFieldPtr()->clear();
01996
01997         for(z = 0, t=getOriginTexY(); z < getWidth(); z++, t+=tstep)
01998         {
01999             for(x = 0, s=getOriginTexX(); x < getWidth(); x++, s+=sstep)
02000             {
02001                 tex->push_back(Vec2f(s, t));
02002             }
02003         }
02004
02005
02006
02007        setTexCoords(tex);
02008
02009        if(getPerPixelLighting())
02010        {
02011            // modify material
02012            if(getMaterial() != NULL)
02013            {
02014                addMaterialChunks();
02015
02016                setNormals(NULL);
02017            }
02018        }
02019        else
02020        {
02021            // create vertex normals
02022            GeoPnt3fProperty *pos  =
02023                dynamic_cast<GeoPnt3fProperty *>(getPositions());
02024
02025            GeoVec3fPropertyUnrecPtr norm = GeoVec3fProperty::create();
02026
02027            norm->editFieldPtr()->clear();
02028
02029            for(z = 0; z < getWidth(); z++)
02030            {
02031                for(x = 0; x < getWidth(); x++, vnum++)
02032                {
02033                    Vec3f tanx, tany, n;
02034                    if(x > 0)
02035                    {
02036                        tanx = pos->getValue(vnum) - pos->getValue(vnum-1);
02037                    }
02038                    else
02039                    {
02040                        tanx = pos->getValue(vnum+1) - pos->getValue(vnum);
02041                    }
02042                    if(z > 0)
02043                    {
02044                        tany =
02045                            pos->getValue(vnum) -
02046                            pos->getValue(vnum-getWidth());
02047                    }
02048                    else
02049                    {
02050                        tany =
02051                            pos->getValue(vnum+getWidth()) -
02052                            pos->getValue(vnum);
02053                    }
02054
02055                    n.setValue(tany.cross(tanx)); n.normalize();
02056                    norm->push_back(n);
02057                }
02058            }
02059
02060            setNormals(norm);
02061        }
02062
02063     }
02064     Inherited::changed(whichField, origin, details);
02065 }
02066
02067 void QuadTreeTerrain::dump(      UInt32    ,
02068                          const BitVector ) const
02069 {
02070     SLOG << "Dump QuadTreeTerrain NI" << std::endl;
02071 }