Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

To rid of the triple and plane struct in types.h. now using the generic ones

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.