Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8775 was 8775, checked in by ponder, 18 years ago
  • Applied a small change to sounrd_buffer and ogg_player in order to load the buffers correctly on big endian systems.
  • Did a lot of cleanup for the terrain rendering system. Added destructors and constructors.
  • The cameras clip-space has to be set to a very large range to display the skybox. The maximum clip distance for the terrain needs to be smaller.
File size: 13.4 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        heightfield.height = tmpData->h;
42        heightfield.width = tmpData->w;
43        heightfield.pitch = tmpData->pitch;
44        int dataSize = heightfield.pitch*heightfield.height;
45        heightfield.data = new Uint8[dataSize];
46        memcpy( heightfield.data, tmpData->pixels, sizeof(Uint8)*dataSize );
47        SDL_FreeSurface( tmpData );
48        pagesX = (heightfield.width/(pageSize-1) );
49        pagesZ = (heightfield.height/(pageSize-1) );
50       
51       
52        pages = new pTerrainPage[pagesX*pagesZ];       
53        for ( int x = 0; x < pagesX; ++x )
54                for ( int z = 0; z < pagesZ; ++z )
55                        pages[z*pagesX+x] = createPage( x, z );
56        //Inform each page about its neighbors.
57        for ( int x = 0; x < pagesX; ++x )
58                for ( int z = 0; z < pagesZ; ++z )
59                        pages[z*pagesX+x]->setNeighbors(
60                                x > 0                   ? getPage( x-1, z+0 ) : NULL,
61                                x < pagesX-1    ? getPage( x+1, z+0 ) : NULL, 
62                                z < pagesZ-1    ? getPage( x+0, z+1 ) : NULL,
63                                z > 0                   ? getPage( x+0, z-1 ) : NULL );
64       
65        root = createQuadTree( 0, 0, pagesX, pagesZ );
66       
67        for ( unsigned int i = 0; i < layers.size(); ++i ) {
68                determineLayerVisibility( i );
69        }       
70        activePages = NULL;
71}
72
73pTerrainQuad Terrain::createQuadTree( int _x0, int _z0, int _x1, int _z1, int _depth )
74{
75        int _x01 = (_x1+_x0)/2; int _z01 = (_z1+_z0)/2;
76        pTerrainQuad node;
77        if ( _x1-_x0 == 1 ) {
78                node = getPage( _x0, _z0 );
79                node->setChildren( NULL, NULL, NULL, NULL );
80        }
81        else { 
82                node = new TerrainQuad( this, _x0, _z0, _x1, _z1 );
83                node->setChildren( 
84                        createQuadTree( _x0, _z0, _x01, _z01, _depth+1 ),
85                        createQuadTree( _x01, _z0, _x1, _z01, _depth+1 ), 
86                        createQuadTree( _x0, _z01, _x01, _z1, _depth+1 ), 
87                        createQuadTree( _x01, _z01, _x1, _z1, _depth+1 ) );
88        }
89        node->calculateBounds();
90        return node;
91}
92
93pTerrainPage Terrain::createPage( int _xOffset, int _zOffset ) const
94{
95        pTerrainPage newPage = new TerrainPage( const_cast<Terrain*>( this ), _xOffset, _zOffset );
96        newPage->setScale( scale );     
97        newPage->setPosition( Triple( scale.x*_xOffset, 0.0f, scale.z*_zOffset ) );
98        newPage->calculateErrors();
99        return newPage;
100}
101
102void Terrain::addLevelFourPage( int _numVertices, Vertex *_vertices, 
103        int _numIndices, unsigned short *_indices )
104{
105        assert( indices ); assert( vertices );
106        BufferInfo bi = buffers[current];       
107        if ( ( MAX_VERTICES < _numVertices+bi.numVertices ) || 
108                ( MAX_INDICES < _numIndices+bi.numIndices+2 ) ) {
109                //So, we need the next vb and ib. Lets put the old into vram...
110                glBindBufferARB( GL_ARRAY_BUFFER_ARB, bi.vbIdentifier );
111                glBufferDataARB( GL_ARRAY_BUFFER_ARB, MAX_VERTICES*sizeof( Vertex ), 
112                        vertices, GL_DYNAMIC_DRAW_ARB );
113                glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, bi.ibIdentifier );
114                glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, MAX_INDICES*sizeof( short ), 
115                        indices, GL_DYNAMIC_DRAW_ARB );
116                BufferInfo newInfo;
117                broker->acquire( newInfo.vbIdentifier, newInfo.ibIdentifier );                 
118                current++;     
119                buffers.push_back( newInfo );   
120                bi = newInfo;
121        }
122        //For the vertex data, a simple copy operation is sufficient...
123        memcpy( &vertices[bi.numVertices], _vertices, 
124                _numVertices*sizeof( Vertex ) );
125        //The indices need to be updated with an offset :(
126        unsigned short *dst= &indices[bi.numIndices];
127        unsigned short offset = bi.numVertices;
128        unsigned short *src= _indices;
129        unsigned short *end= src+_numIndices;
130        bi.numVertices+=_numVertices;           
131        if ( bi.numIndices ) {
132                dst[0] = *(dst-1);
133                dst[1] = *src+offset;
134                dst+=2; bi.numIndices+=2;
135        }
136        while ( src < end ) {
137                *dst= *src+offset;
138                dst++;
139                src++;
140        }
141        bi.numIndices+=_numIndices;
142        buffers[current] = bi;
143}
144
145void Terrain::determineVisiblePages( pTerrainQuad _node )
146{
147        switch( _node->cull() ) {
148                case Frustum::INTERSECT:
149                        //printf( "partially inside frustum\n" );
150                        if ( !_node->isChildless() ) {
151                                pTerrainQuad *children = _node->getChildren();
152                                for ( int i = 0; i < 4; ++i, ++children )
153                                        determineVisiblePages( *children );
154                        }
155                        else {
156                                showPages( _node->getXOffset(), 
157                                                   _node->getZOffset(), 1, 1 );
158                        }
159                        break;
160                case Frustum::INSIDE:
161                        //printf( "fully inside frustum\n" );                   
162                        showPages( _node->getXOffset(), 
163                                           _node->getZOffset(), 
164                                           _node->getWidth() , 
165                                           _node->getHeight() );
166                        break;
167                case Frustum::OUTSIDE:
168                        cullCount+= (_node->getWidth()-1)*(_node->getHeight()-1);
169                        break;
170        }
171}
172
173void Terrain::draw( )
174{
175        glGetError();
176        pTerrainPage page = NULL;
177        frustum->extractPlanes();
178        glPushAttrib( GL_ALL_ATTRIB_BITS );
179        glPushClientAttrib( GL_CLIENT_VERTEX_ARRAY_BIT );
180        /*
181         * Enable texture and vertex arrays for the first and the second texture
182         * units and disable the normal arrays.
183         */
184        glClientActiveTextureARB( GL_TEXTURE0_ARB );   
185                glEnableClientState( GL_VERTEX_ARRAY );
186                glEnableClientState( GL_TEXTURE_COORD_ARRAY ); 
187                glDisableClientState( GL_NORMAL_ARRAY );
188                               
189        glClientActiveTextureARB( GL_TEXTURE1_ARB );   
190                glEnableClientState( GL_VERTEX_ARRAY );
191                glEnableClientState( GL_TEXTURE_COORD_ARRAY );
192                glDisableClientState( GL_NORMAL_ARRAY ); 
193        glDisable( GL_CULL_FACE );
194        glCullFace( GL_BACK );
195        glDisable( GL_LIGHTING );
196        glColor3f( 1.0f, 1.0f, 1.0f );
197        //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
198        cullCount = 0;
199       
200        // Lets see which pages are visible.
201        determineVisiblePages( root );
202        int wantedLeft, wantedRight, wantedBottom, wantedTop, minLOD;
203        pTerrainPage neighbor = NULL;
204        page = activePages;
205        bool dirty;
206        current = 0;
207        BufferInfo bi;
208        broker->acquire( bi.vbIdentifier, bi.ibIdentifier );
209        buffers.push_back( bi );
210        int dirtyRounds = 0;
211        do {
212                dirtyRounds++;
213                dirty = false;
214                page = activePages;
215                while ( page ) {
216                        if ( !page->isActive() ) {
217                                pTerrainPage tmp = page;
218                                page = tmp->getNext();
219                                tmp->setVisibility( false );
220                                continue;
221                        }
222                        wantedLeft = wantedRight = wantedBottom = wantedTop = page->getWantedLOD();
223                        if ( ( neighbor = page->getLeft() ) && ( neighbor->isActive() ) ) 
224                                wantedLeft = neighbor->getWantedLOD();
225                        if ( ( neighbor = page->getRight() ) && ( neighbor->isActive() ) ) 
226                                wantedRight = neighbor->getWantedLOD();
227                        if ( ( neighbor = page->getTop() ) && ( neighbor->isActive() ) ) 
228                                wantedTop = neighbor->getWantedLOD();
229                        if ( ( neighbor = page->getBottom() ) && ( neighbor->isActive() ) ) 
230                                wantedBottom = neighbor->getWantedLOD();                       
231               
232                        minLOD = std::min( std::min( wantedBottom, wantedTop ), 
233                                std::min( wantedLeft, wantedRight ) ); 
234                        if ( minLOD < page->getWantedLOD()-1 ) {
235                                page->setWantedLOD( minLOD+1 );
236                                dirty = true;
237                        }       
238                        page = page->getNext();
239                }
240        } while ( dirty );
241       
242        page = activePages;
243        while ( page ) {
244                assert( page->isActive() );
245                page->updateTesselation();
246                page = page->getNext();
247        }
248        //If there is some data in the buffer, we need to upload the data
249        //into the vram...
250        if ( buffers[current].numIndices != 0 ) {
251                BufferInfo bi = buffers[current];       
252                glBindBufferARB( GL_ARRAY_BUFFER_ARB, bi.vbIdentifier );
253                glBufferDataARB( GL_ARRAY_BUFFER_ARB, MAX_VERTICES*sizeof( Vertex ), 
254                        vertices, GL_DYNAMIC_DRAW_ARB );
255                glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, bi.ibIdentifier );
256                glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, MAX_INDICES*sizeof( short ), 
257                        indices, GL_DYNAMIC_DRAW_ARB );
258        }
259        glEnable( GL_BLEND );
260        glDepthFunc( GL_LEQUAL );
261        glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
262        for ( unsigned int i = 0; i < layers.size(); ++i ) {
263                LayerInfo* layer= layers[i];
264                page = activePages;     
265                               
266                glActiveTextureARB( GL_TEXTURE1_ARB );
267                glClientActiveTextureARB( GL_TEXTURE1_ARB );
268                if ( layer->detail ) {
269                        //glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND );
270                        glEnable( GL_TEXTURE_2D );
271                        glBindTexture( GL_TEXTURE_2D, layer->detail->getTexture() );
272                        glMatrixMode( GL_TEXTURE );
273                        glLoadIdentity();
274                        glScalef( layer->repeatX, layer->repeatZ, 1.0f );
275                }       
276                else {
277                        glDisable( GL_TEXTURE_2D );
278                }
279
280                glClientActiveTextureARB( GL_TEXTURE0_ARB );
281                glActiveTextureARB( GL_TEXTURE0_ARB );
282                glEnable( GL_TEXTURE_2D );
283                if ( layer->alpha ) {
284                        //glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND );                   
285                        glBindTexture( GL_TEXTURE_2D, layer->alpha->getTexture() );
286                }
287                else {
288               
289                        glBindTexture( GL_TEXTURE_2D, lightmap->getTexture() );                 
290                }
291                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );                         
292                glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
293                glEnable( GL_CULL_FACE );
294                for ( unsigned j = 0; j < buffers.size(); ++j ) {
295                        BufferInfo bi = buffers[j];
296                        glBindBufferARB( GL_ARRAY_BUFFER_ARB, bi.vbIdentifier );
297                        glClientActiveTextureARB( GL_TEXTURE0_ARB );
298                        glInterleavedArrays( GL_T2F_V3F, 0, NULL );
299
300                        glClientActiveTextureARB( GL_TEXTURE1_ARB );
301                        glInterleavedArrays( GL_T2F_V3F, 0, NULL );
302
303                        glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 
304                                bi.ibIdentifier );     
305
306                        glDrawElements( GL_TRIANGLE_STRIP, bi.numIndices, 
307                                                        GL_UNSIGNED_SHORT, NULL );
308                }
309                while ( page ) {
310                        if ( page->hasMaterial( i ) )
311                                page->draw();
312                        page = page->getNext();
313                }
314                activatedCount = 0; deactivatedCount = 0;
315        }
316       
317        for ( unsigned int i = 0; i < buffers.size(); ++i )
318                broker->release( buffers[i].vbIdentifier, buffers[i].ibIdentifier );
319               
320        buffers.clear();
321               
322        glClientActiveTextureARB( GL_TEXTURE1_ARB );   
323        glActiveTextureARB( GL_TEXTURE1_ARB );
324        glDisable( GL_TEXTURE_2D );
325        glActiveTextureARB( GL_TEXTURE0_ARB );
326        glEnable( GL_LIGHTING );
327        glMatrixMode( GL_TEXTURE );
328        glLoadIdentity();
329        glPopAttrib();
330        glPopClientAttrib();
331}
332
333inline Uint8 getAlpha( const SDL_Surface *_s, int _x, int _y )
334{
335    int bpp = _s->format->BytesPerPixel;
336    Uint8 *p = (Uint8 *)_s->pixels + _y*_s->pitch + _x * bpp;
337        Uint32 pixel = 0;
338    switch( bpp ) {
339    case 1:
340        pixel = *p;
341                break;
342    case 2:
343        pixel = *(Uint16 *)p;
344                break;
345    case 3:
346        if( SDL_BYTEORDER == SDL_BIG_ENDIAN )
347            pixel = p[0] << 16 | p[1] << 8 | p[2];
348        else
349            pixel = p[0] | p[1] << 8 | p[2] << 16;
350                break;
351    case 4:
352        pixel = *(Uint32 *)p;
353                break;
354    default:
355        return 255;       /* shouldn't happen, but avoids warnings */
356    }
357        Uint8 r,g,b,a;
358        SDL_GetRGBA( pixel, _s->format, &r, &g, &b, &a );
359        return a;
360}
361
362void Terrain::determineLayerVisibility( int _layer )
363{
364        LayerInfo * layer = layers[_layer];
365        if ( !layer->alpha ) {
366                int numPages = pagesX*pagesZ;
367                for ( int i = 0; i < numPages; ++i )
368                        pages[i]->setLayerVisibility( _layer, LV_FULL );
369                       
370                return;
371        }
372        SDL_Surface *alpha = const_cast<SDL_Surface*>( layer->alpha->getStoredImage() );       
373        SDL_LockSurface( alpha );
374        float du = ( (float)alpha->w)/pagesX;
375        float dv = ( (float)alpha->h)/pagesZ;
376        float u = 0.0f, v = 0.0f;
377        for ( int pageX = 0; pageX < pagesX; ++pageX ) {
378                v = 0.0f;
379                for ( int pageZ = 0; pageZ < pagesZ; ++pageZ ) {
380                        bool full = true; bool has = false;
381                        for ( int x = 0; x < (int)PAGE_SIZE; ++x ) {
382                                for ( int z = 0; z < (int)PAGE_SIZE; ++z ) {
383                                        Uint8 a = getAlpha( alpha, (int)u, (int)v );
384                                        if ( a ) 
385                                                has = true;
386                                        if ( a < 250 )
387                                                full = false;
388                                }
389                        }
390                        LayerVisibility lv;
391                        if ( has ) {
392                                if ( full )
393                                        lv = LV_FULL;
394                                else
395                                        lv = LV_PARTIAL;
396                        }
397                        else {
398                                lv = LV_NO;
399                        }
400                        getPage( pageX, pageZ )->setLayerVisibility( _layer, lv );
401                        v+= dv;
402                }
403                u+= du;
404        }
405        SDL_UnlockSurface( alpha );
406}
407
408void Terrain::getAltitude( Triple& _alt, Triple& _normal )
409{
410        float xScaled = _alt.x / scale.x, zScaled = _alt.z / scale.z;
411        //The offset on the map
412        int xOff =  (int)xScaled, zOff = (int)zScaled;
413        //The interpolation values.
414        float u = xScaled-xOff, v = zScaled-zOff;
415       
416        float dX = scale.x / ( pageSize-1 );
417        float dZ = scale.z / ( pageSize-1 );
418       
419        //If u is bigger than v, we are on the lower triangle...
420        if ( u > v ) {
421               
422                float alt[] = {
423                        getAltitude( xOff+0, zOff+0 )*scale.y,
424                        getAltitude( xOff+1, zOff+0 )*scale.y,
425                        getAltitude( xOff+1, zOff+1 )*scale.y };
426                _alt.y = (1.0f-u-v)*alt[0]+u*alt[1]+v*alt[2];
427               
428                //Since we know about the directions of some x and z-coordinates,
429                //not the whole cross products needs to be calculated. Grab yourself
430                //pen and paper :)
431                _normal.x = -dZ*( alt[0] - alt[1] );                           
432                _normal.y = -dZ*dX;
433                _normal.z =  dX*( alt[2] - alt[1] );
434        }
435        else {
436                float alt[] = {
437                        getAltitude( xOff+0, zOff+0 )*scale.y,
438                        getAltitude( xOff+0, zOff+1 )*scale.y,
439                        getAltitude( xOff+1, zOff+1 )*scale.y };                       
440                _alt.y = (1.0f-u-v)*alt[0]+v*alt[1]+u*alt[2];
441                //Since we know about the directions of some x and z-coordinates,
442                //not the whole cross products needs to be calculated. Grab yourself
443                //pen and paper :)
444                _normal.x =  dZ*( alt[2] - alt[1] );                           
445                _normal.y = -dZ*dX;
446                _normal.z = -dX*( alt[0] - alt[1] );
447        }
448}
449
450void Terrain::showPages( int _x0, int _z0, int _width, int _height )
451{
452        for ( int x = 0; x < _width; ++x ) {
453                for ( int z = 0; z < _height; ++z ) {
454                        pTerrainPage page = getPage( _x0+x, _z0+z );
455                        page->setVisibility( true );                   
456                        page->chooseLOD();
457
458                }
459        }
460}
Note: See TracBrowser for help on using the repository browser.