Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/terrain/src/lib/graphics/importer/terrain/terrain_page.cc @ 8349

Last change on this file since 8349 was 8349, checked in by ponder, 18 years ago
  • I changend the tesselation method. Now the tesselation level of the pages neighbors are taken into account. This is neccessary for the geomorphing.
  • Fixed a bug, that caused the visible pages to be put two times into the active pages list.
File size: 15.3 KB
Line 
1/*
2        orxonox - the future of 3D-vertical-scrollers
3 
4        Copyright (C) 2006 orx
5 
6        This program is free software; you can redistribute it and/or modify
7        it under the terms of the GNU General Public License as published by
8        the Free Software Foundation; either version 2, or (at your option)
9        any later version.
10 
11        ### File Specific:
12        main-programmer: Marco Biasini
13 
14 */
15#include "terrain_page.h"
16#include "terrain.h"
17#include "glincl.h"
18#include <stdio.h>
19#include <math.h>
20
21#define CHECK_GL_ERROR( _d ) do { \
22        GLenum __err = glGetError(); \
23        if ( __err != GL_NO_ERROR ) \
24                printf( "check%s: %s\n", _d, (char*)gluErrorString( __err ) );\
25        }\
26        while ( 0 )
27
28TerrainPage::TerrainPage( Terrain *_owner, int _xOffset, int _zOffset )
29: TerrainQuad( _owner, _xOffset, _zOffset )
30{
31        scale = owner->getScale();
32        numVertices = numIndices = 0;
33        errors = new LODError[TerrainPage::MAX_LODS];
34        vertices = NULL; indices = NULL; coords = NULL;
35        position = Triple( scale.x*_xOffset, 0.0f, scale.z*_zOffset );
36        isVisible = false;
37        next = NULL;
38        active = false;
39        previous = NULL;
40        currentLOD = -1;
41        forceTesselation = true;
42}
43
44void TerrainPage::tesselateRow( int _z, int _xStride, int _zStride, bool _adaptLeft, bool _adaptRight )
45{
46        int xStart = 0, xEnd = owner->getPageSize();
47       
48        int halfStride = _zStride >> 1;
49
50
51        if ( _z ) {
52                addAgain( );
53                addIndex( getIndex( 0, _z ) );
54        }
55        if ( _adaptLeft ) {
56                assert( halfStride > 0 );               
57                addIndex( getIndex( 0, _z ) );
58                addIndex( getIndex( 0, _z+halfStride ) );
59                addIndex( getIndex( _xStride, _z ) );
60                addIndex( getIndex( 0, _z+_zStride ) );
61                addIndex( getIndex( _xStride, _z+_zStride ) );
62                addAgain();
63                xStart = _xStride;
64        }
65       
66        if ( _adaptRight )
67                xEnd-=_xStride;
68               
69        for ( int x = xStart; x < xEnd; x+=_xStride ) {
70                addIndex( getIndex( x, _z) );
71                addIndex( getIndex( x, _z+_zStride ) );
72        }
73       
74        int w = owner->getPageSize()-1;
75       
76        if ( _adaptRight ) {
77                assert( halfStride > 0 );               
78                addIndex( getIndex( w-_xStride, _z ) );
79                addAgain();
80                addIndex( getIndex( w, _z ) );
81                addIndex( getIndex( w-_xStride, _z+_zStride ) );
82                addIndex( getIndex( w, _z+halfStride ) );
83                addIndex( getIndex( w, _z+_zStride ) );
84        }
85}
86
87//TODO: Perform benchmark to measure if the isVisible test should be included
88//in this method.
89
90void TerrainPage::determineBorderAdaption( bool _adapt[] )
91{
92        _adapt[0] = _adapt[1] = _adapt[2] = _adapt[3] = false;
93       
94        if ( left  && left->isVisible ) 
95                _adapt[TP_LEFT] = ( wantedLOD - left->getWantedLOD() ) > 0;
96               
97        if ( right && right->isVisible ) 
98                _adapt[TP_RIGHT] = ( wantedLOD - right->getWantedLOD() ) > 0;
99               
100        if ( top && top->isVisible) 
101                _adapt[TP_TOP] = ( wantedLOD - top->getWantedLOD() ) > 0;                               
102               
103        if ( bottom && bottom->isVisible ) 
104                _adapt[TP_BOTTOM] = ( wantedLOD - bottom->getWantedLOD() ) > 0;         
105}
106
107int TerrainPage::chooseLOD()
108{
109        wantedLOD = -1;
110        Triple cam( owner->getCameraPosition() );
111       
112        for ( int i = TerrainPage::MAX_LODS-1; i >= 0; --i ) {
113                Triple distance( cam.x-errors[i].correct.x,
114                                                 cam.y-errors[i].correct.y,
115                                                 cam.z-errors[i].correct.z );
116               
117                float d = distance.length();
118               
119                assert( d > 0.000001f );
120                float err =  errors[i].diff / d ;
121               
122                if ( err*scale.y < owner->getDetail() ) {
123                        wantedLOD = i;
124                        break;
125                }       
126        }
127        if ( wantedLOD < 0 ) {
128                wantedLOD = TerrainPage::MAX_LODS-1;
129        }       
130        // Calculate the tween factor. The calculation is different if LOD is 0
131        //
132        if ( wantedLOD > 0 ) {
133               
134        }
135        else {
136               
137        }
138        return wantedLOD;
139}
140
141void TerrainPage::activate()
142{
143       
144        pTerrainPage list = owner->getActiveList();
145        next = list;
146        if ( list )
147                list->previous = this;
148        owner->setActiveList( this );
149}
150
151void TerrainPage::deactivate()
152{
153        pTerrainPage list = owner->getActiveList();
154       
155        if ( previous ) {
156                previous->next = next;
157        }
158        if ( next ) {
159                next->previous = previous;
160        }
161        if ( list == this )
162                owner->setActiveList( next );
163        next = NULL;
164        previous = NULL;
165       
166}
167
168void TerrainPage::calculateError( int _lod )
169{
170        float   sumError = 0.0f;
171        int             numErrors = 0;
172        int size = owner->getPageSize();
173        if( _lod!=0 )
174        {
175                int stride = 1 << _lod, x0, y0, xi, yi;
176                // Altough these four nested loops look very scary, they're not
177                // that bad and require only about O(n^2).
178                for( y0 = 0 ; y0 < size-stride; y0 += stride ) {
179                        for( x0 = 0; x0 < size-stride; x0 += stride ) {
180                                for( yi = 1; yi < stride; yi++ ) {
181                                        for( xi = 1; xi < stride; xi++ )
182                                        {
183                                                int     x = x0+xi,
184                                                y = y0+yi;
185                                                float   fx0 = ( float )xi/( float )stride, fx1 = 1.0f-fx0,
186                                                                fy0 = ( float )yi/( float )stride, fy1 = 1.0f-fy0;
187                                               
188                                                float   height00 = getAltitude( x0, y0 ),
189                                                                height10 = getAltitude( x0+stride,y0 ),
190                                                                height01 = getAltitude( x0,y0+stride ),
191                                                                height11 = getAltitude( x0+stride, y0+stride );
192                                               
193                                                float   paintHeight =   fx1*fy1 * height00 +
194                                                        fx0*fy1 * height10 +
195                                                        fx1*fy0 * height01 +
196                                                        fx0*fy0 * height11,
197                                                        correctHeight =  getAltitude( x, y );
198                                               
199                                                float   er = ( float )fabs( correctHeight - paintHeight );
200                                               
201                                                numErrors++;
202                                                sumError += er;
203                                        }
204                                }
205                        }
206                }
207                float error = sumError / numErrors;
208                getVertex(size/2, size/2, errors[_lod].correct);
209                errors[_lod].real = errors[_lod].correct;
210                errors[_lod].real.y += error;
211                errors[_lod].diff = error;
212        }
213}
214
215void TerrainPage::calculateBounds()
216{
217        int size = owner->getPageSize();
218        float alt = 0.0f;
219       
220        Triple  min( xOffset*scale.x ,0.0f, zOffset*scale.z ), 
221                        max( (xOffset+1)*scale.x, 0.0f, (zOffset+1)*scale.z);
222       
223        min.y = max.y = getAltitude( 0, 0 );
224        for ( int x = 0; x < size; ++x ) {
225                for ( int z = 0; z < size; ++z ) {
226                        alt = getAltitude( x, z );
227                        min.y = fmin( min.y, alt );
228                        max.y = fmax( max.y, alt );
229                }
230        }
231        bounds.set( min, max );
232}
233
234float TerrainPage::getAltitude( int _x, int _z ) const
235{
236        assert( _x >= 0 && _x < 17 );
237        assert( _z >= 0 && _z < 17 );
238        return position.y+scale.y*owner->getAltitude( 
239                        _x+(owner->getPageSize()-1)*xOffset, 
240                        _z+(owner->getPageSize()-1)*zOffset );
241}
242
243bool TerrainPage::needsRetesselation()
244{
245       
246        bool    leftChanged     = ( left && left->isVisible ) && ( left->wantedLOD != left->currentLOD ),
247                        rightChanged    = ( right && right->isVisible ) && ( right->wantedLOD != right->currentLOD ),
248                        topChanged              = ( top && top->isVisible ) && ( top->wantedLOD != currentLOD ),
249                        bottomChanged   = ( bottom && bottom->isVisible ) && ( bottom->wantedLOD != bottom->currentLOD ),
250                        iChanged                =  wantedLOD != currentLOD;
251                               
252        return ( leftChanged || rightChanged || topChanged || bottomChanged || forceTesselation || iChanged );
253}
254
255void TerrainPage::updateTesselation( )
256{
257
258        if ( needsRetesselation() ) {
259                tesselate( wantedLOD );
260        }       
261        currentLOD = wantedLOD;
262        forceTesselation = false;
263}
264
265void TerrainPage::tesselateLevelFourPatch( bool _adapt[] )
266{
267        const int       halfStride = 8, stride = 16;
268       
269        enum { ADAPT_L = 1, ADAPT_R = 2, ADAPT_B = 4, ADAPT_T = 8,
270                ADAPT_LR = 3, ADAPT_LB = 5, ADAPT_LT = 9, ADAPT_RB = 6, 
271                ADAPT_RT = 10, ADAPT_BT = 12, ADAPT_LRB = 7, ADAPT_LBT = 13, ADAPT_LRT = 11, 
272                ADAPT_RBT = 14, ADAPT_LRBT = 15, ADAPT_NONE = 0 };
273               
274        int                     code =  ( _adapt[TP_LEFT]       ? ADAPT_L : 0 ) | 
275                                                ( _adapt[TP_RIGHT]      ? ADAPT_R : 0 ) | 
276                                                ( _adapt[TP_BOTTOM] ? ADAPT_B : 0 ) | 
277                                                ( _adapt[TP_TOP]        ? ADAPT_T : 0 );
278        switch( code ) {
279                case ADAPT_NONE:
280                        addIndex( getIndex( 0, 0 ) );
281                        addIndex( getIndex( 0, stride ) );
282                        addIndex( getIndex( stride, 0 ) );
283                        addIndex( getIndex( stride, stride ) );
284                        break;
285                       
286                case ADAPT_L:
287                        addIndex( getIndex( 0, 0 ) );
288                        addIndex( getIndex( 0, halfStride ) );
289                        addIndex( getIndex( stride, 0 ) );
290                        addIndex( getIndex( 0, stride ) );
291                        addIndex( getIndex( stride, stride ) );
292                        addAgain(  );
293                        break;
294               
295                case ADAPT_R:
296                        addIndex( getIndex( stride, stride ) );
297                        addIndex( getIndex( stride, halfStride ) );
298                        addIndex( getIndex( 0, stride ) );
299                        addIndex( getIndex( stride, 0 ) );
300                        addIndex( getIndex( 0, 0 ) );
301                        addAgain(  );
302                        break;
303
304                case ADAPT_LR:
305                        addIndex( getIndex( 0, 0 ) );
306                        addIndex( getIndex( 0, halfStride ) );
307                        addIndex( getIndex( stride, 0 ) );
308                        addIndex( getIndex( 0, stride ) );
309                        addIndex( getIndex( stride, halfStride ) );
310                        addIndex( getIndex( stride, stride ) );
311                        break;
312
313                case ADAPT_B:
314                        addIndex( getIndex( 0, 0 ) );
315                        addAgain(  );
316                        addIndex( getIndex( halfStride, 0 ) );
317                        addIndex( getIndex( 0, stride ) );
318                        addIndex( getIndex( stride, 0 ) );
319                        addIndex( getIndex( stride, stride ) );
320                        break;
321
322                case ADAPT_LB:
323                        addIndex( getIndex( 0, 0 ) );
324                        addIndex( getIndex( 0, halfStride ) );
325                        addIndex( getIndex( halfStride, 0 ) );
326                        addIndex( getIndex( 0, stride ) );
327                        addIndex( getIndex( stride, 0 ) );
328                        addIndex( getIndex( stride, stride ) );
329                        break;
330
331                case ADAPT_RB:
332                        addIndex( getIndex( 0, 0 ) );
333                        addIndex( getIndex( 0, stride ) );
334                        addIndex( getIndex( halfStride, 0 ) );
335                        addIndex( getIndex( stride, 0 ) );
336                        addAgain(  );
337                        addIndex( getIndex( 0, stride ) );
338                        addIndex( getIndex( stride, halfStride ) );
339                        addIndex( getIndex( stride, stride ) );
340                        break;
341
342                case ADAPT_LRB:
343                        addIndex( getIndex( stride, stride ) );
344                        addIndex( getIndex( stride, halfStride ) );
345                        addIndex( getIndex( 0, stride ) );
346                        addIndex( getIndex( stride, 0 ) );
347                        addIndex( getIndex( 0, halfStride ) );
348                        addIndex( getIndex( halfStride, 0 ) );
349                        addIndex( getIndex( 0, 0 ) );
350                        addAgain(  );
351                        break;
352
353                case ADAPT_T:
354                        addIndex( getIndex( stride, stride ) );
355                        addAgain(  );
356                        addIndex( getIndex( halfStride, stride ) );
357                        addIndex( getIndex( stride, 0 ) );
358                        addIndex( getIndex( 0, stride ) );
359                        addIndex( getIndex( 0, 0 ) );
360                        break;
361
362                case ADAPT_LT:
363                        addIndex( getIndex( stride, stride ) );
364                        addIndex( getIndex( stride, 0 ) );
365                        addIndex( getIndex( halfStride, stride ) );
366                        addIndex( getIndex( 0, stride ) );
367                        addAgain(  );
368                        addIndex( getIndex( stride, 0 ) );
369                        addIndex( getIndex( 0, halfStride ) );
370                        addIndex( getIndex( 0, 0 ) );
371                        break;
372
373                case ADAPT_RT:
374                        addIndex( getIndex( stride, stride ) );
375                        addIndex( getIndex( stride, halfStride ) );
376                        addIndex( getIndex( halfStride, stride ) );
377                        addIndex( getIndex( stride, 0 ) );
378                        addIndex( getIndex( 0, stride ) );
379                        addIndex( getIndex( 0, 0 ) );
380                        break;
381
382                case ADAPT_LRT:
383                        addIndex( getIndex( 0, 0 ) );
384                        addIndex( getIndex( 0, halfStride ) );
385                        addIndex( getIndex( stride, 0 ) );
386                        addIndex( getIndex( 0, stride ) );
387                        addIndex( getIndex( stride, halfStride ) );
388                        addIndex( getIndex( halfStride, stride ) );
389                        addIndex( getIndex( stride, stride ) );
390                        addAgain(  );
391                        break;
392
393                case ADAPT_BT:
394                        addIndex( getIndex( 0, 0 ) );
395                        addAgain(  );
396                        addIndex( getIndex( halfStride, 0 ) );
397                        addIndex( getIndex( 0, stride ) );
398                        addIndex( getIndex( stride, 0 ) );
399                        addIndex( getIndex( halfStride, stride ) );
400                        addIndex( getIndex( stride, stride ) );
401                        addAgain(  );
402                        break;
403
404                case ADAPT_LBT:
405                        addIndex( getIndex( 0, 0 ) );
406                        addIndex( getIndex( 0, halfStride ) );
407                        addIndex( getIndex( halfStride, 0 ) );
408                        addIndex( getIndex( 0, stride ) );
409                        addIndex( getIndex( stride, 0 ) );
410                        addIndex( getIndex( halfStride, stride ) );
411                        addIndex( getIndex( stride, stride ) );
412                        addAgain(  );
413                        break;
414
415                case ADAPT_RBT:
416                        addIndex( getIndex( stride, stride ) );
417                        addIndex( getIndex( stride, halfStride ) );
418                        addIndex( getIndex( halfStride, stride ) );
419                        addIndex( getIndex( stride, 0 ) );
420                        addIndex( getIndex( 0, stride ) );
421                        addIndex( getIndex( halfStride, 0 ) );
422                        addIndex( getIndex( 0, 0 ) );
423                        addAgain(  );
424                        break;
425
426                case ADAPT_LRBT:
427                        addIndex( getIndex( 0, 0 ) );
428                        addIndex( getIndex( 0, halfStride ) );
429                        addIndex( getIndex( halfStride, 0 ) );
430                        addIndex( getIndex( 0, stride ) );
431                        addIndex( getIndex( stride, 0 ) );
432                        addIndex( getIndex( halfStride, stride ) );
433                        addIndex( getIndex( stride, halfStride ) );
434                        addIndex( getIndex( stride, stride ) );
435                        break;
436        }
437}
438
439
440void TerrainPage::tesselate( int _lod )
441{
442
443        int count = owner->getPageSize()*owner->getPageSize(); 
444
445        memset( indexHash, 0xffff, 
446                        sizeof(unsigned short)*count );
447       
448        numVertices = 0; numIndices = 0;
449       
450        //Calculate the pace, based on the lod.
451        int stride = 1 << _lod;
452               
453        bool adapt[4];
454        determineBorderAdaption( adapt );
455        assert( isVisible );
456        if ( _lod == TerrainPage::MAX_LODS-1 ) {
457                tesselateLevelFourPatch( adapt );
458                return;
459        }
460       
461        int zStart = 0, zEnd = owner->getPageSize()-stride;
462       
463        if ( adapt[TP_TOP] ) {
464                tesselateRow( 0, stride / 2, stride, adapt[TP_LEFT], adapt[TP_RIGHT] );
465                zStart+= stride;
466        }
467       
468        if ( adapt[TP_BOTTOM] )
469                zEnd-= stride;
470               
471        for ( int z = zStart; z < zEnd; z+=stride )
472                tesselateRow( z, stride, stride, adapt[TP_LEFT], adapt[TP_RIGHT] );
473       
474
475        if ( !adapt[TP_BOTTOM] )
476                return;
477               
478        addAgain( );
479        addIndex( getIndex( 0, owner->getPageSize()-1-stride ) );
480       
481        tesselateRow( owner->getPageSize()-1-stride, stride / 2, stride, adapt[TP_LEFT], adapt[TP_RIGHT] );
482
483}
484
485
486void TerrainPage::show( )
487{
488        int count = owner->getPageSize()*owner->getPageSize();
489       
490        vertices = new Triple[count];
491        // Not the economical way, but we just want to have space for all indices.
492        indices = new unsigned short[count*3];
493        indexHash = new unsigned short[count]; 
494        coords = new TexCoord[count];
495        forceTesselation = true;
496        activate();
497        active = true;
498}
499
500void TerrainPage::hide( )
501{
502        if ( vertices ) {
503                delete[] vertices;
504                vertices = NULL;
505        }
506        delete[] coords;       
507        if ( indices ) {
508                delete[] indices;
509                indices = NULL;
510                numIndices = 0;
511        }
512        deactivate();
513}
514
515void TerrainPage::drawBox()
516{
517        glMatrixMode( GL_MODELVIEW );
518        glPushMatrix();
519        glTranslatef( bounds.corner.x, bounds.corner.y, bounds.corner.z );
520        glBegin( GL_QUADS );
521                glVertex3f( 0.0f, 0.0f, 0.0f );
522                glVertex3f(  bounds.x, 0.0f, 0.0f );
523                glVertex3f( bounds.x, bounds.y, 0.0f );
524                glVertex3f( 0.0f, bounds.y, 0.0f );
525               
526                glVertex3f( 0.0f, 0.0f, bounds.z );
527                glVertex3f(  bounds.x, 0.0f, bounds.z );
528                glVertex3f( bounds.x, bounds.y, bounds.z );
529                glVertex3f( 0.0f, bounds.y, bounds.z );         
530               
531                glVertex3f( 0.0f, 0.0f, 0.0 );
532                glVertex3f(  0.0, 0.0f, bounds.z );
533                glVertex3f( 0.0f, bounds.y, bounds.z );
534                glVertex3f( 0.0f, bounds.y, 0.0f );                             
535               
536                glVertex3f( bounds.x, 0.0f, 0.0 );
537                glVertex3f(  bounds.x, 0.0f, bounds.z );
538                glVertex3f( bounds.x, bounds.y, bounds.z );
539                glVertex3f( bounds.x, bounds.y, 0.0f );                                         
540        glEnd();
541        glPopMatrix();
542}
543
544void TerrainPage::draw( )
545{
546        //These give an error
547        assert( glIsEnabled( GL_VERTEX_ARRAY ) );
548        assert( !glIsEnabled( GL_NORMAL_ARRAY ) );
549        assert( isVisible ); assert( numIndices > 0 );
550        assert( active );
551        active = false;
552        CHECK_GL_ERROR( "1" ); 
553        glVertexPointer( 3, GL_FLOAT, 0, vertices );
554        glTexCoordPointer( 2, GL_FLOAT, 0, coords );   
555        glDrawElements( GL_TRIANGLE_STRIP, numIndices, 
556                                        GL_UNSIGNED_SHORT, indices );
557       
558        if ( owner->debug() )
559                drawBox( );
560               
561        CHECK_GL_ERROR( "2" ); 
562}
563
564short TerrainPage::getIndex( int _x, int _z )
565{
566        unsigned short index = _z*owner->getPageSize()+_x;
567        if ( indexHash[index] == 0xffff ) {
568                //The vertex didn't exists before, lets create it.
569                indexHash[index] = numVertices;
570                getVertex( _x, _z, vertices[numVertices] );
571                getCoord( _x, _z, coords[numVertices] );
572                numVertices++;
573        }
574        return indexHash[index];
575}
576
577void TerrainPage::getCoord( int _x, int _z, TexCoord& _coord ) const 
578{
579        owner->getCoord( _x+xOffset*(owner->getPageSize()-1 ), 
580                                         _z+zOffset*(owner->getPageSize()-1 ), _coord );
581}
582
583void TerrainPage::getVertex( int _x, int _z, Triple& _vertex ) const
584{
585        _vertex.x = position.x+scale.x*_x/
586                ( owner->getPageSize()-1 );
587        _vertex.y = getAltitude( _x, _z );
588        _vertex.z = position.z+scale.z*_z/( owner->getPageSize()-1 );
589}
590
591void TerrainPage::calculateErrors()
592{
593        for ( int i = 0; i < TerrainPage::MAX_LODS; ++i )
594                calculateError( i );
595}
Note: See TracBrowser for help on using the repository browser.