PageRenderTime 121ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Lab_1/HeightMapModel.cpp

https://github.com/abcfantasy/ANDgine
C++ | 316 lines | 216 code | 48 blank | 52 comment | 62 complexity | 081325f0ddad88ab4f70e6c1b203ce71 MD5 | raw file
  1. #include "HeightMapModel.h"
  2. #include "ResourceManager.h"
  3. #include "TextureResource.h"
  4. #include "TGAImage.h"
  5. #include "Math.h"
  6. #include "SDL.h"
  7. #include "HeightMapGen.h"
  8. int HeightMapModel::NEXT_DIRECTION = HeightMapModel::DIRECTION_NORTH;
  9. float* HeightMapModel::PREVIOUS_MAP = NULL;
  10. // Easy access to a particular vertex
  11. // heightmap( i, j ) = the vertex at position i,j
  12. Vertex3f* HeightMapModel::operator()( int i, int j ) {
  13. return &this->vertices_[ j * this->width_ + i ];
  14. };
  15. // Overrides the method from Model, because we're rendering with a triangle strip and in a different order
  16. void HeightMapModel::compile(){
  17. // We need to free up the old display list if we were using one
  18. if( this->displayListId_ != Model::INVALID_HANDLE ) {
  19. glDeleteLists( this->displayListId_, 1 );
  20. }
  21. this->displayListId_ = glGenLists( 1 );
  22. glNewList( this->displayListId_, GL_COMPILE );
  23. // TODO: figure out how to work materials into this
  24. //float specReflection[] = { 0.2f, 0.2f, 0.2f, 1.0f };
  25. //glMaterialfv(GL_FRONT, GL_SPECULAR, specReflection);
  26. //glMateriali(GL_FRONT, GL_SHININESS, 129 );
  27. // If we have a texture, we're using it, but if we don't we're not using it
  28. // The code here may be duplicated but it works faster than it would be if we're making a check for every vertex
  29. if ( this->texture_ != NULL ) {
  30. int i, j;
  31. Vertex3f *point;
  32. glBindTexture( GL_TEXTURE_2D, this->texture_->getTexture()->TextureID );
  33. for ( i = 0 ; i < this->length_ - 1; ++i ) {
  34. glBegin(GL_TRIANGLE_STRIP);
  35. for ( j = 0; j < this->width_; ++j ) {
  36. point = (*this)( i + 1, j );
  37. glColor3f( point->getR(), point->getG(), point->getB() );
  38. glTexCoord2f( point->getU(), point->getV() );
  39. glNormal3f( point->getNormalX(), point->getNormalY(), point->getNormalZ() );
  40. glVertex3f( point->getX(), point->getY(), point->getZ() );
  41. point = (*this)( i, j );
  42. glColor3f( point->getR(), point->getG(), point->getB() );
  43. glTexCoord2f( point->getU(), point->getV() );
  44. glNormal3f( point->getNormalX(), point->getNormalY(), point->getNormalZ() );
  45. glVertex3f( point->getX(), point->getY(), point->getZ() );
  46. }
  47. glEnd();
  48. }
  49. } else {
  50. int i, j;
  51. Vertex3f *point;
  52. for ( i = 0 ; i < this->length_ - 1; ++i ) {
  53. glBegin(GL_TRIANGLE_STRIP);
  54. for ( j = 0; j < this->width_; ++j ) {
  55. point = (*this)( i + 1, j );
  56. glColor3f( point->getR(), point->getG(), point->getB() );
  57. glNormal3f( point->getNormalX(), point->getNormalY(), point->getNormalZ() );
  58. glVertex3f( point->getX(), point->getY(), point->getZ() );
  59. point = (*this)( i, j );
  60. glColor3f( point->getR(), point->getG(), point->getB() );
  61. glNormal3f( point->getNormalX(), point->getNormalY(), point->getNormalZ() );
  62. glVertex3f( point->getX(), point->getY(), point->getZ() );
  63. }
  64. glEnd();
  65. }
  66. }
  67. glEndList();
  68. };
  69. // Calculates the normals for lighting; this method is magic :D
  70. // TODO: Still needs some optimization
  71. void HeightMapModel::computeNormals() {
  72. float norm1[3], norm2[3], norm3[3], norm4[3];
  73. int i, j;
  74. Math::makeNullVector( norm1 );
  75. Math::makeNullVector( norm2 );
  76. Math::makeNullVector( norm3 );
  77. Math::makeNullVector( norm4 );
  78. for(i = 0; i < this->length_; i++)
  79. for(j = 0; j < this->width_; j++) {
  80. // normals for the four corners
  81. if( i == 0 && j == 0 ) {
  82. Math::crossProduct( (*this)( 0, 0 )->getPosition(), (*this)( 0, 1 )->getPosition(), (*this)( 1, 0 )->getPosition(), norm1 );
  83. } else if( j == this->width_ - 1 && i == this->length_ - 1 ) {
  84. Math::crossProduct( (*this)( i, j )->getPosition(), (*this)( j, i - 1 )->getPosition(), (*this)( j - 1, i )->getPosition(), norm1 );
  85. } else if( j == 0 && i == this->length_ - 1 ) {
  86. Math::crossProduct( (*this)( i, j )->getPosition(), (*this)( j, i - 1 )->getPosition(), (*this)( j + 1, i )->getPosition(), norm1 );
  87. } else if( j == this->width_ - 1 && i == 0 ) {
  88. Math::crossProduct( (*this)( i, j )->getPosition(), (*this)( j, i + 1 )->getPosition(), (*this)( j - 1, i )->getPosition(), norm1 );
  89. }
  90. // normals for the borders
  91. else if( i == 0 ) {
  92. Math::crossProduct( (*this)( j, 0 )->getPosition(), (*this)( j - 1, 0 )->getPosition(), (*this)( j, 1 )->getPosition(), norm1 );
  93. Math::crossProduct( (*this)( j, 0 )->getPosition(), (*this)( j, 1 )->getPosition(), (*this)( j + 1, 0 )->getPosition(), norm2 );
  94. } else if( j == 0 ) {
  95. Math::crossProduct( (*this)( 0, i )->getPosition(), (*this)( 1, i )->getPosition(), (*this)( 0, i - 1 )->getPosition(), norm1 );
  96. Math::crossProduct( (*this)( 0, i )->getPosition(), (*this)( 0, i + 1 )->getPosition(), (*this)( 1, i )->getPosition(), norm2 );
  97. } else if( i == this->length_ ) {
  98. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j, i - 1 )->getPosition(), (*this)( j + 1, i )->getPosition(), norm1 );
  99. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j + 1, i )->getPosition(), (*this)( j, i - 1 )->getPosition(), norm2 );
  100. } else if( j == this->width_ ) {
  101. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j, i - 1 )->getPosition(), (*this)( j - 1, i )->getPosition(), norm1 );
  102. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j - 1, i )->getPosition(), (*this)( j, i + 1 )->getPosition(), norm2 );
  103. }
  104. // normals for the interior
  105. else {
  106. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j - 1, i )->getPosition(), (*this)( j, i + 1 )->getPosition(), norm1 );
  107. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j, i + 1 )->getPosition(), (*this)( j + 1, i )->getPosition(), norm2 );
  108. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j + 1, i )->getPosition(), (*this)( j, i - 1 )->getPosition(), norm3 );
  109. Math::crossProduct( (*this)( j, i )->getPosition(), (*this)( j, i - 1 )->getPosition(), (*this)( j - 1, i )->getPosition(), norm4 );
  110. }
  111. Math::normalize( norm1 );
  112. Math::normalize( norm2 );
  113. Math::normalize( norm3 );
  114. Math::normalize( norm4 );
  115. Math::addVector( norm1, norm2 );
  116. Math::addVector( norm1, norm3 );
  117. Math::addVector( norm1, norm4 );
  118. Math::makeNullVector( norm2 );
  119. Math::makeNullVector( norm3 );
  120. Math::makeNullVector( norm4 );
  121. Math::normalize(norm1);
  122. norm1[2] = -norm1[2];
  123. (*this)( i, j )->setNormal( norm1 );
  124. }
  125. };
  126. bool HeightMapModel::load() {
  127. if( this->filename_[0] ) return loadFromImage();
  128. else return loadFromSeed();
  129. };
  130. bool HeightMapModel::loadFromSeed() {
  131. float map[257 * 257];
  132. // If you pass "size" to the function, the size of the map should be ( size + 1 )^2
  133. // SDL_GetTicks() is a pretty random seed
  134. // High h => smooth map
  135. // Low h => rough map
  136. if( PREVIOUS_MAP != NULL ) {
  137. switch( NEXT_DIRECTION ) {
  138. case DIRECTION_NORTH:
  139. HeightMapGen::generateHeightMapNorth( map, 256, SDL_GetTicks(), 1, 1.0f, PREVIOUS_MAP );
  140. break;
  141. case DIRECTION_SOUTH:
  142. HeightMapGen::generateHeightMapSouth( map, 256, SDL_GetTicks(), 1, 1.0f, PREVIOUS_MAP );
  143. break;
  144. case DIRECTION_EAST:
  145. HeightMapGen::generateHeightMapEast( map, 256, SDL_GetTicks(), 1, 1.0f, PREVIOUS_MAP );
  146. break;
  147. case DIRECTION_WEST:
  148. HeightMapGen::generateHeightMapWest( map, 256, SDL_GetTicks(), 1, 1.0f, PREVIOUS_MAP );
  149. break;
  150. }
  151. delete[] PREVIOUS_MAP;
  152. PREVIOUS_MAP = NULL;
  153. } else
  154. HeightMapGen::generateHeightMap( map, 256, SDL_GetTicks(), 1, 1.0f );
  155. //HeightMapGen::generateHeightMap( map, 256, SDL_GetTicks(), 1, 1.0f );
  156. this->width_ = this->length_ = 257;
  157. float textureIncrementX = 1.0f / this->width_;
  158. float textureIncrementZ = 1.0f / this->length_;
  159. float xOffset = 0.0f;
  160. float yOffset = 0.0f;
  161. // This calculates the amplitude of the scaling
  162. float amp = this->maxScale_ - this->minScale_;
  163. // We're applying all the modifications to a single vertex
  164. // The vertices in the array are copies anyway
  165. Vertex3f point;
  166. // We start calculating the vertices
  167. for (int i = 0 ; i < this->length_; ++i)
  168. for (int j = 0;j < this->width_; ++j ) {
  169. // The X and Z positions depend on the point we're at
  170. point.setX( float(j) + xOffset );
  171. point.setZ( float(i) + xOffset );
  172. // The Y position is interpolated using the amplitude
  173. point.setY( amp * (map[(this->width_ * j) + i]) + minScale_ );
  174. // The texture coordinates are also easy
  175. point.setU( j * textureIncrementX );
  176. point.setV( i * textureIncrementZ );
  177. //point.setR( 1.0f );
  178. //point.setG( 0.5f );
  179. //point.setB( 0.5f );
  180. this->addVertex( point );
  181. }
  182. // We need to calculate the normals for lighting
  183. this->computeNormals();
  184. // The rendering is done as a triangle strip
  185. this->renderMethod_ = GL_TRIANGLE_STRIP;
  186. return true;
  187. };
  188. bool HeightMapModel::loadFromImage() {
  189. TGAImage *tgaImage = ResourceManager::instance()->get<TGAImage>( this->filename_ );
  190. if( tgaImage == NULL )
  191. return false;
  192. // The width and length of the model (in units) are the same as the values in pixels
  193. this->width_ = tgaImage->width_;
  194. this->length_ = tgaImage->height_;
  195. // The texture coordinates will be at fixed increments which we compute here
  196. float textureIncrementX = 1.0f / this->width_;
  197. float textureIncrementZ = 1.0f / this->length_;
  198. // The model will be centered around the origin
  199. float xOffset = 0.0f;//-(this->width_ / 2.0f);
  200. float zOffset = 0.0f;//-(this->length_ / 2.0f);
  201. // This calculates the amplitude of the scaling
  202. float amp = this->maxScale_ - this->minScale_;
  203. // This keeps track of the byte we're at in the image
  204. int pixel = 0;
  205. // We're applying all the modifications to a single vertex
  206. // The vertices in the array are copies anyway
  207. Vertex3f point;
  208. // We start calculating the vertices
  209. for (int i = 0 ; i < this->length_; ++i)
  210. for (int j = 0;j < this->width_; ++j, pixel += tgaImage->bytesPerPx_ ) {
  211. // The X and Z positions depend on the pixel we're at
  212. point.setX( float(j) + xOffset );
  213. point.setZ( float(i) + zOffset );
  214. // The Y position scales with the last component of the pixel
  215. // And is interpolated using the amplitude
  216. point.setY( amp * ( tgaImage->image_[pixel + ( tgaImage->bytesPerPx_ - 1 ) ] / 256.0f ) );
  217. // The texture coordinates are also easy
  218. point.setU( j * textureIncrementX );
  219. point.setV( i * textureIncrementZ );
  220. // If mode = RGBA then we fill in the colors as well
  221. if ( tgaImage->bytesPerPx_ == 4 ) {
  222. point.setR( tgaImage->image_[pixel] / 256.0f );
  223. point.setG( tgaImage->image_[pixel + 1] / 256.0f );
  224. point.setB( tgaImage->image_[pixel + 2] / 256.0f );
  225. }
  226. this->addVertex( point );
  227. }
  228. // We're done with the image now
  229. ResourceManager::instance()->remove( tgaImage );
  230. // We need to calculate the normals for lighting
  231. this->computeNormals();
  232. // The rendering is done as a triangle strip
  233. this->renderMethod_ = GL_TRIANGLE_STRIP;
  234. return true;
  235. };
  236. // Gets the interpolated height at the coordinates given
  237. float HeightMapModel::getHeight( float x, float z ) {
  238. int coordX, coordZ;
  239. float scaleX, scaleZ;
  240. float a, b, c, d;
  241. if( x < 0 || z < 0 || x >= width_ || z >= width_ )
  242. return 0;
  243. coordX = (int)x;
  244. coordZ = (int)z;
  245. scaleX = (float)x - coordX;
  246. scaleZ = (float)z - coordZ;
  247. a = (*this)( coordX, coordZ )->getY();
  248. b = (*this)( coordX + 1, coordZ )->getY();
  249. c = (*this)( coordX, coordZ + 1 )->getY();
  250. d = (*this)( coordX + 1, coordZ + 1 )->getY();
  251. return a * ( 1 - scaleX ) * ( 1 - scaleZ ) + b * scaleX * ( 1 - scaleZ ) + c * ( 1 - scaleX ) * scaleZ + d * scaleX * scaleZ;
  252. };
  253. // Rescales the model between min and max
  254. void HeightMapModel::rescale( float minScale, float maxScale ) {
  255. float oldAmp = this->maxScale_ - this->minScale_;
  256. float newAmp = maxScale - minScale;
  257. for( std::vector<Vertex3f>::iterator i = this->vertices_.begin(); i != this->vertices_.end(); ++i )
  258. i->setY( i->getY() * newAmp / oldAmp );
  259. this->minScale_ = minScale;
  260. this->maxScale_ = maxScale;
  261. };
  262. // Special collision method - we're not using a bounding box for this kind of model
  263. // We're checking the height at each point
  264. bool HeightMapModel::checkSpecialCollision( float *position ) {
  265. float height = getHeight( position[0], position[2] );
  266. if( height > position[1] + 0.2 ) return true;
  267. return false;
  268. };