Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8965 was 8965, checked in by ponder, 18 years ago

Added debug information to the terrain

File size: 15.8 KB
Line 
1
2#include "terrain.h"
3#include "terrain_page.h"
4#include "glincl.h"
5#include "util/loading/resource_manager.h"
6#include "debug.h"
7#include <math.h>
8#ifdef HAVE_SDL_SDL_IMAGE_H
9#include <SDL/SDL_image.h>
10#else
11#include <SDL_image.h>
12#endif
13
14bool validateSize( int _s ) {
15        _s-=1;
16        int s = 16;
17        while ( s <= _s ) {
18                if (s == _s )
19                        return true;
20                s*=2;
21        }
22        return false;
23}
24void Terrain::build()
25{
26        ResourceManager *MANAGER = ResourceManager::getInstance();
27        std::string full = MANAGER->getFullName( heightmapSource );
28        SDL_Surface *tmpData = IMG_Load( full.c_str() );
29        if ( !tmpData ) {
30                PRINTF(0)( "I' sorry, I can't load %s\n", full.c_str() );
31                return;
32        }               
33        if ( !validateSize( tmpData->h ) || !validateSize( tmpData->w ) ) {
34                PRINTF(0)( "The size of the elevation map must be 2^n+1x2^m+1. and at least 17x17" );
35                return;
36        }
37        if ( tmpData->format->BytesPerPixel != 1 ) {
38                PRINTF(0)( "The elevetation map must be an 8bit image!" );
39                return;
40        }
41        PRINTF(2)( "Loaded the elevation map\n" );
42        heightfield.height = tmpData->h;
43        heightfield.width = tmpData->w;
44        heightfield.pitch = tmpData->pitch;
45        int dataSize = heightfield.pitch*heightfield.height;
46        heightfield.data = new Uint8[dataSize];
47        memcpy( heightfield.data, tmpData->pixels, sizeof(Uint8)*dataSize );
48        SDL_FreeSurface( tmpData );
49        pagesX = (heightfield.width/(pageSize-1) );
50        pagesZ = (heightfield.height/(pageSize-1) );
51       
52       
53        pages = new pTerrainPage[pagesX*pagesZ];       
54        for ( int x = 0; x < pagesX; ++x )
55                for ( int z = 0; z < pagesZ; ++z )
56                        pages[z*pagesX+x] = createPage( x, z );
57        //Inform each page about its neighbors.
58        for ( int x = 0; x < pagesX; ++x )
59                for ( int z = 0; z < pagesZ; ++z )
60                        pages[z*pagesX+x]->setNeighbors(
61                                x > 0                   ? getPage( x-1, z+0 ) : NULL,
62                                x < pagesX-1    ? getPage( x+1, z+0 ) : NULL, 
63                                z < pagesZ-1    ? getPage( x+0, z+1 ) : NULL,
64                                z > 0                   ? getPage( x+0, z-1 ) : NULL );
65       
66        root = createQuadTree( 0, 0, pagesX, pagesZ );
67       
68        for ( unsigned int i = 0; i < layers.size(); ++i ) {
69                determineLayerVisibility( i );
70        }       
71        activePages = NULL;
72}
73
74pTerrainQuad Terrain::createQuadTree( int _x0, int _z0, int _x1, int _z1, int _depth )
75{
76        int _x01 = (_x1+_x0)/2; int _z01 = (_z1+_z0)/2;
77        pTerrainQuad node;
78        if ( _x1-_x0 == 1 ) {
79                node = getPage( _x0, _z0 );
80                node->setChildren( NULL, NULL, NULL, NULL );
81        }
82        else { 
83                node = new TerrainQuad( this, _x0, _z0, _x1, _z1 );
84                node->setChildren( 
85                        createQuadTree( _x0, _z0, _x01, _z01, _depth+1 ),
86                        createQuadTree( _x01, _z0, _x1, _z01, _depth+1 ), 
87                        createQuadTree( _x0, _z01, _x01, _z1, _depth+1 ), 
88                        createQuadTree( _x01, _z01, _x1, _z1, _depth+1 ) );
89        }
90        node->calculateBounds();
91        return node;
92}
93
94pTerrainPage Terrain::createPage( int _xOffset, int _zOffset ) const
95{
96        pTerrainPage newPage = new TerrainPage( const_cast<Terrain*>( this ), _xOffset, _zOffset );
97        newPage->setScale( scale );     
98        newPage->setPosition( Triple( scale.x*_xOffset, 0.0f, scale.z*_zOffset ) );
99        newPage->calculateErrors();
100        return newPage;
101}
102
103void Terrain::addLevelFourPage( int _numVertices, Vertex *_vertices, 
104        int _numIndices, unsigned short *_indices )
105{
106        assert( indices ); assert( vertices );
107        BufferInfo bi = buffers[current];       
108        if ( ( MAX_VERTICES < _numVertices+bi.numVertices ) || 
109                ( MAX_INDICES < _numIndices+bi.numIndices+2 ) ) {
110                //So, we need the next vb and ib. Lets put the old into vram...
111                glBindBufferARB( GL_ARRAY_BUFFER_ARB, bi.vbIdentifier );
112                glBufferDataARB( GL_ARRAY_BUFFER_ARB, MAX_VERTICES*sizeof( Vertex ), 
113                        vertices, GL_DYNAMIC_DRAW_ARB );
114                glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, bi.ibIdentifier );
115                glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, MAX_INDICES*sizeof( short ), 
116                        indices, GL_DYNAMIC_DRAW_ARB );
117                BufferInfo newInfo;
118                broker->acquire( newInfo.vbIdentifier, newInfo.ibIdentifier );                 
119                current++;     
120                buffers.push_back( newInfo );   
121                bi = newInfo;
122        }
123        //For the vertex data, a simple copy operation is sufficient...
124        memcpy( &vertices[bi.numVertices], _vertices, 
125                _numVertices*sizeof( Vertex ) );
126        //The indices need to be updated with an offset :(
127        unsigned short *dst= &indices[bi.numIndices];
128        unsigned short offset = bi.numVertices;
129        unsigned short *src= _indices;
130        unsigned short *end= src+_numIndices;
131        bi.numVertices+=_numVertices;           
132        if ( bi.numIndices ) {
133                dst[0] = *(dst-1);
134                dst[1] = *src+offset;
135                dst+=2; bi.numIndices+=2;
136        }
137        while ( src < end ) {
138                *dst= *src+offset;
139                dst++;
140                src++;
141        }
142        bi.numIndices+=_numIndices;
143        buffers[current] = bi;
144}
145
146void Terrain::determineVisiblePages( pTerrainQuad _node )
147{
148        switch( _node->cull() ) {
149                case Frustum::INTERSECT:
150                        //printf( "partially inside frustum\n" );
151                        if ( !_node->isChildless() ) {
152                                pTerrainQuad *children = _node->getChildren();
153                                for ( int i = 0; i < 4; ++i, ++children )
154                                        determineVisiblePages( *children );
155                        }
156                        else {
157                                showPages( _node->getXOffset(), 
158                                                   _node->getZOffset(), 1, 1 );
159                        }
160                        break;
161                case Frustum::INSIDE:
162                        //printf( "fully inside frustum\n" );                   
163                        showPages( _node->getXOffset(), 
164                                           _node->getZOffset(), 
165                                           _node->getWidth() , 
166                                           _node->getHeight() );
167                        break;
168                case Frustum::OUTSIDE:
169                        cullCount+= (_node->getWidth()-1)*(_node->getHeight()-1);
170                        break;
171        }
172}
173
174void Terrain::tick( float _dt )
175{
176        for ( unsigned int i = 0; i < buffers.size(); ++i )
177                broker->release( buffers[i].vbIdentifier, buffers[i].ibIdentifier );
178        buffers.clear();       
179        pTerrainPage page = NULL;
180        //Extract the frustum planes out of the modelview matrix.
181        frustum->extractPlanes();       
182        // Lets see which pages are visible.
183        determineVisiblePages( root );
184        int wantedLeft, wantedRight, wantedBottom, wantedTop, minLOD;
185        pTerrainPage neighbor = NULL;
186        page = activePages;
187        bool dirty;
188        current = 0;
189        BufferInfo bi;
190        broker->acquire( bi.vbIdentifier, bi.ibIdentifier );
191        buffers.push_back( bi );
192        int dirtyRounds = 0;
193        do {
194                dirtyRounds++;
195                dirty = false;
196                page = activePages;
197                while ( page ) {
198                        if ( !page->isActive() ) {
199                                pTerrainPage tmp = page;
200                                page = tmp->getNext();
201                                tmp->setVisibility( false );
202                                continue;
203                        }
204                        wantedLeft = wantedRight = wantedBottom = wantedTop = page->getWantedLOD();
205                        if ( ( neighbor = page->getLeft() ) && ( neighbor->isActive() ) ) 
206                                wantedLeft = neighbor->getWantedLOD();
207                        if ( ( neighbor = page->getRight() ) && ( neighbor->isActive() ) ) 
208                                wantedRight = neighbor->getWantedLOD();
209                        if ( ( neighbor = page->getTop() ) && ( neighbor->isActive() ) ) 
210                                wantedTop = neighbor->getWantedLOD();
211                        if ( ( neighbor = page->getBottom() ) && ( neighbor->isActive() ) ) 
212                                wantedBottom = neighbor->getWantedLOD();                       
213               
214                        minLOD = std::min( std::min( wantedBottom, wantedTop ), 
215                                std::min( wantedLeft, wantedRight ) ); 
216                        if ( minLOD < page->getWantedLOD()-1 ) {
217                                page->setWantedLOD( minLOD+1 );
218                                dirty = true;
219                        }       
220                        page = page->getNext();
221                }
222        } while ( dirty );
223       
224        page = activePages;
225        while ( page ) {
226                assert( page->isActive() );
227                page->updateTesselation();
228                page = page->getNext();
229        }
230        //If there is some data in the buffer, we need to upload the data
231        //into the vram...
232        if ( buffers[current].numIndices != 0 ) {
233                BufferInfo bi = buffers[current];       
234                glBindBufferARB( GL_ARRAY_BUFFER_ARB, bi.vbIdentifier );
235                glBufferDataARB( GL_ARRAY_BUFFER_ARB, MAX_VERTICES*sizeof( Vertex ), 
236                        vertices, GL_DYNAMIC_DRAW_ARB );
237                glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, bi.ibIdentifier );
238                glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, MAX_INDICES*sizeof( short ), 
239                        indices, GL_DYNAMIC_DRAW_ARB );
240        }       
241}
242void Terrain::draw( )
243{
244        pTerrainPage page = NULL;
245        glGetError();
246        /*pTerrainPage page = NULL;
247        frustum->extractPlanes();*/
248        Plane far = frustum->getPlane( Frustum::FAR );
249        //Due to some reason, the OpenGL implementors chose the plane equation
250        //to an array of doubles. So we will make them happy.
251        double farPlane[] = { far.n.x, far.n.y, far.n.z, far.d };
252        glEnable( GL_CLIP_PLANE0 );
253        glClipPlane( GL_CLIP_PLANE0, farPlane );
254        glPushAttrib( GL_ALL_ATTRIB_BITS );
255        glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
256        /*
257         * Enable texture and vertex arrays for the first and the second texture
258         * units and disable the normal arrays.
259         */
260        glClientActiveTextureARB( GL_TEXTURE0_ARB );   
261                glEnableClientState( GL_VERTEX_ARRAY );
262                glEnableClientState( GL_TEXTURE_COORD_ARRAY ); 
263                glDisableClientState( GL_NORMAL_ARRAY );
264                               
265        glClientActiveTextureARB( GL_TEXTURE1_ARB );   
266                glEnableClientState( GL_VERTEX_ARRAY );
267                glEnableClientState( GL_TEXTURE_COORD_ARRAY );
268                glDisableClientState( GL_NORMAL_ARRAY ); 
269        glDisable( GL_CULL_FACE );
270        glCullFace( GL_BACK );
271        glDisable( GL_LIGHTING );
272        glColor3f( 1.0f, 1.0f, 1.0f );
273        //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
274        cullCount = 0;
275       
276        /*// Lets see which pages are visible.
277        determineVisiblePages( root );
278        int wantedLeft, wantedRight, wantedBottom, wantedTop, minLOD;
279        pTerrainPage neighbor = NULL;
280        page = activePages;
281        bool dirty;
282        current = 0;
283        BufferInfo bi;
284        broker->acquire( bi.vbIdentifier, bi.ibIdentifier );
285        buffers.push_back( bi );
286        int dirtyRounds = 0;
287        do {
288                dirtyRounds++;
289                dirty = false;
290                page = activePages;
291                while ( page ) {
292                        if ( !page->isActive() ) {
293                                pTerrainPage tmp = page;
294                                page = tmp->getNext();
295                                tmp->setVisibility( false );
296                                continue;
297                        }
298                        wantedLeft = wantedRight = wantedBottom = wantedTop = page->getWantedLOD();
299                        if ( ( neighbor = page->getLeft() ) && ( neighbor->isActive() ) )
300                                wantedLeft = neighbor->getWantedLOD();
301                        if ( ( neighbor = page->getRight() ) && ( neighbor->isActive() ) )
302                                wantedRight = neighbor->getWantedLOD();
303                        if ( ( neighbor = page->getTop() ) && ( neighbor->isActive() ) )
304                                wantedTop = neighbor->getWantedLOD();
305                        if ( ( neighbor = page->getBottom() ) && ( neighbor->isActive() ) )
306                                wantedBottom = neighbor->getWantedLOD();                       
307               
308                        minLOD = std::min( std::min( wantedBottom, wantedTop ),
309                                std::min( wantedLeft, wantedRight ) ); 
310                        if ( minLOD < page->getWantedLOD()-1 ) {
311                                page->setWantedLOD( minLOD+1 );
312                                dirty = true;
313                        }       
314                        page = page->getNext();
315                }
316        } while ( dirty );
317       
318        page = activePages;
319        while ( page ) {
320                assert( page->isActive() );
321                page->updateTesselation();
322                page = page->getNext();
323        }
324        //If there is some data in the buffer, we need to upload the data
325        //into the vram...
326        if ( buffers[current].numIndices != 0 ) {
327                BufferInfo bi = buffers[current];       
328                glBindBufferARB( GL_ARRAY_BUFFER_ARB, bi.vbIdentifier );
329                glBufferDataARB( GL_ARRAY_BUFFER_ARB, MAX_VERTICES*sizeof( Vertex ),
330                        vertices, GL_DYNAMIC_DRAW_ARB );
331                glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, bi.ibIdentifier );
332                glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, MAX_INDICES*sizeof( short ),
333                        indices, GL_DYNAMIC_DRAW_ARB );
334        }*/
335        glEnable( GL_BLEND );
336        glDepthFunc( GL_LEQUAL );
337        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
338        for ( unsigned int i = 0; i < layers.size(); ++i ) {
339                LayerInfo* layer= layers[i];
340                page = activePages;     
341                               
342                glActiveTextureARB( GL_TEXTURE1_ARB );
343                glClientActiveTextureARB( GL_TEXTURE1_ARB );
344                if ( layer->detail ) {
345                        //glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND );
346                        glEnable( GL_TEXTURE_2D );
347                        glBindTexture( GL_TEXTURE_2D, layer->detail->getTexture() );
348                        glMatrixMode( GL_TEXTURE );
349                        glLoadIdentity();
350                        glScalef( layer->repeatX, layer->repeatZ, 1.0f );
351                }       
352                else {
353                        glDisable( GL_TEXTURE_2D );
354                }
355
356                glClientActiveTextureARB( GL_TEXTURE0_ARB );
357                glActiveTextureARB( GL_TEXTURE0_ARB );
358                glEnable( GL_TEXTURE_2D );
359                if ( layer->alpha ) {
360                        //glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND );                   
361                        glBindTexture( GL_TEXTURE_2D, layer->alpha->getTexture() );
362                }
363                else {
364               
365                        glBindTexture( GL_TEXTURE_2D, lightmap->getTexture() );                 
366                }
367                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );                         
368                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
369                glEnable( GL_CULL_FACE );
370                for ( unsigned j = 0; j < buffers.size(); ++j ) {
371                        BufferInfo bi = buffers[j];
372                        glBindBufferARB( GL_ARRAY_BUFFER_ARB, bi.vbIdentifier );
373                        glClientActiveTextureARB( GL_TEXTURE0_ARB );
374                        glInterleavedArrays( GL_T2F_V3F, 0, NULL );
375
376                        glClientActiveTextureARB( GL_TEXTURE1_ARB );
377                        glInterleavedArrays( GL_T2F_V3F, 0, NULL );
378
379                        glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 
380                                bi.ibIdentifier );     
381
382                        glDrawElements( GL_TRIANGLE_STRIP, bi.numIndices, 
383                                                        GL_UNSIGNED_SHORT, NULL );
384                }
385                while ( page ) {
386                        if ( page->hasMaterial( i ) )
387                                page->draw();
388                        page = page->getNext();
389                }
390        }
391                       
392        glClientActiveTextureARB( GL_TEXTURE1_ARB );   
393        glActiveTextureARB( GL_TEXTURE1_ARB );
394        glDisable( GL_TEXTURE_2D );
395        glActiveTextureARB( GL_TEXTURE0_ARB );
396        glEnable( GL_LIGHTING );
397        glMatrixMode( GL_TEXTURE );
398        glLoadIdentity();
399        glPopAttrib();
400        glPopClientAttrib();
401        glDisable( GL_CLIP_PLANE0 );
402}
403
404inline Uint8 getAlpha( const SDL_Surface *_s, int _x, int _y )
405{
406    int bpp = _s->format->BytesPerPixel;
407    Uint8 *p = (Uint8 *)_s->pixels + _y*_s->pitch + _x * bpp;
408        Uint32 pixel = 0;
409    switch( bpp ) {
410    case 1:
411        pixel = *p;
412                break;
413    case 2:
414        pixel = *(Uint16 *)p;
415                break;
416    case 3:
417        if( SDL_BYTEORDER == SDL_BIG_ENDIAN )
418            pixel = p[0] << 16 | p[1] << 8 | p[2];
419        else
420            pixel = p[0] | p[1] << 8 | p[2] << 16;
421                break;
422    case 4:
423        pixel = *(Uint32 *)p;
424                break;
425    default:
426        return 255;       /* shouldn't happen, but avoids warnings */
427    }
428        Uint8 r,g,b,a;
429        SDL_GetRGBA( pixel, _s->format, &r, &g, &b, &a );
430        return a;
431}
432
433void Terrain::determineLayerVisibility( int _layer )
434{
435        LayerInfo * layer = layers[_layer];
436        if ( !layer->alpha ) {
437                int numPages = pagesX*pagesZ;
438                for ( int i = 0; i < numPages; ++i )
439                        pages[i]->setLayerVisibility( _layer, LV_FULL );
440                       
441                return;
442        }
443        SDL_Surface *alpha = const_cast<SDL_Surface*>( layer->alpha->getStoredImage() );       
444        SDL_LockSurface( alpha );
445        float du = ( (float)alpha->w)/pagesX;
446        float dv = ( (float)alpha->h)/pagesZ;
447        float u = 0.0f, v = 0.0f;
448        for ( int pageX = 0; pageX < pagesX; ++pageX ) {
449                v = 0.0f;
450                for ( int pageZ = 0; pageZ < pagesZ; ++pageZ ) {
451                        bool full = true; bool has = false;
452                        for ( int x = 0; x < (int)PAGE_SIZE; ++x ) {
453                                for ( int z = 0; z < (int)PAGE_SIZE; ++z ) {
454                                        Uint8 a = getAlpha( alpha, (int)u, (int)v );
455                                        if ( a ) 
456                                                has = true;
457                                        if ( a < 255 )
458                                                full = false;
459                                }
460                        }
461                        LayerVisibility lv;
462                        if ( has ) {
463                                if ( full )
464                                        lv = LV_FULL;
465                                else
466                                        lv = LV_PARTIAL;
467                        }
468                        else {
469                                lv = LV_NO;
470                        }
471                        getPage( pageX, pageZ )->setLayerVisibility( _layer, lv );
472                        v+= dv;
473                }
474                u+= du;
475        }
476        SDL_UnlockSurface( alpha );
477}
478
479void Terrain::getAltitude( Triple& _alt, Triple& _normal )
480{
481        float xScaled = _alt.x / scale.x, zScaled = _alt.z / scale.z;
482        //The offset on the map
483        int xOff =  (int)xScaled, zOff = (int)zScaled;
484        //The interpolation values.
485        float u = xScaled-xOff, v = zScaled-zOff;
486       
487        float dX = scale.x / ( pageSize-1 );
488        float dZ = scale.z / ( pageSize-1 );
489       
490        //If u is bigger than v, we are on the lower triangle...
491        if ( u > v ) {
492               
493                float alt[] = {
494                        getAltitude( xOff+0, zOff+0 )*scale.y,
495                        getAltitude( xOff+1, zOff+0 )*scale.y,
496                        getAltitude( xOff+1, zOff+1 )*scale.y };
497                _alt.y = (1.0f-u-v)*alt[0]+u*alt[1]+v*alt[2];
498               
499                //Since we know about the directions of some x and z-coordinates,
500                //not the whole cross products needs to be calculated. Grab yourself
501                //pen and paper :)
502                _normal.x =  dZ*( alt[0] - alt[1] );                           
503                _normal.y =  dZ*dX;
504                _normal.z = -dX*( alt[2] - alt[1] );
505        }
506        else {
507                float alt[] = {
508                        getAltitude( xOff+0, zOff+0 )*scale.y,
509                        getAltitude( xOff+0, zOff+1 )*scale.y,
510                        getAltitude( xOff+1, zOff+1 )*scale.y };                       
511                _alt.y = (1.0f-u-v)*alt[0]+v*alt[1]+u*alt[2];
512                //Since we know about the directions of some x and z-coordinates,
513                //not the whole cross products needs to be calculated. Grab yourself
514                //pen and paper :)
515                _normal.x = -dZ*( alt[2] - alt[1] );                           
516                _normal.y =  dZ*dX;
517                _normal.z =  dX*( alt[0] - alt[1] );
518        }
519}
520
521void Terrain::showPages( int _x0, int _z0, int _width, int _height )
522{
523        for ( int x = 0; x < _width; ++x ) {
524                for ( int z = 0; z < _height; ++z ) {
525                        pTerrainPage page = getPage( _x0+x, _z0+z );
526                        page->setVisibility( true );                   
527                        page->chooseLOD();
528
529                }
530        }
531}
Note: See TracBrowser for help on using the repository browser.