Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9414 was 9414, checked in by bensch, 18 years ago

merged back here the terrain.old

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