Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

minor change

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