src/Terrain.cpp

Go to the documentation of this file.
00001 /* Crown and Cutlass
00002  * Terrain Object Code
00003  */
00004 /*
00005 #if defined (WIN32)
00006 #define WIN32_LEAN_AND_MEAN
00007 #include <windows.h>
00008 #endif
00009 */
00010 
00011 #include "SDL_image.h"
00012 #include <iostream>
00013 #include <list>
00014 #include <string>
00015 #include "ccmath.h"
00016 #include "Log.h"
00017 #include "Config.h"
00018 #include "Texture.h"
00019 #include "normals.h"
00020 #include "collisions.h"
00021 #include "QuadNode.h"
00022 #include "BoundBox.h"
00023 #include "Point.h"
00024 #include "Frustum.h"
00025 #include "Ocean.h"
00026 #include "Terrain.h"
00027 
00028 using namespace std;
00029 
00030 // Used to get index in terrain array based on (x, z) coords
00031 //#define TERRAIN(X, Z) terrain[((Z) * w) + (X)]
00032 
00033 // This looks up the normal at (x, z) and gets the Tth element of that point (0 = x, 1 = y, 2 = z)
00034 //#define NORMALS(X, Z, T) normals[(((Z)*w + (X)) * 3) + (T)]
00035 
00036 #define TNORMALS_INDEX(X, Z, T) ((((Z)*m_w + (X)) * 3) + (T))
00037 
00038 #define TERRAIN_ARRAY(X, Z, T) m_terrainArray[((((Z) * m_w) + (X)) * (3+3+2+2)) + (T)]
00039 
00040 #define TERRAIN_ARRAY_INDEX(X, Z) (((Z) * m_w) + (X))
00041 
00042 #define VERTEX(X, Z) m_vertexData[((((Z) * m_w) + (X)) * m_vertexStride) + m_vertexOffset]
00043 
00044 #define ARRAY_STRIDE ((3+3+2+2) * sizeof(GLfloat))
00045 
00046 #define ARRAY_VERTEX 0
00047 #define ARRAY_NORMAL 3
00048 #define ARRAY_TEX0 6
00049 #define ARRAY_TEX1 8
00050 
00051 Terrain::Terrain(string file, GLfloat vScaleIn, int quadSizeIn) {
00052   SDL_Surface *Image;
00053   BoundBox *box;
00054   Uint8 *p;
00055 
00056   // To center the terrain
00057   int xDiff;
00058   int zDiff;
00059 
00060   QuadNode::s_quadSize = quadSizeIn;
00061 
00062   // Load the image, check for errors, if image is not found should throw exception
00063   Image = IMG_Load(file.c_str());
00064   if (!Image) {
00065     throw string("Error: Could not load file " + file);
00066   }
00067 
00068   m_w = Image->w;
00069   m_h = Image->h;
00070 
00071   xDiff = (int) m_w / 2;
00072   zDiff = (int) m_h / 2;
00073 
00074   // Set up the ocean
00075   m_ocean = new Ocean(m_w, m_h);
00076   if (m_ocean == NULL) {
00077     Log::s_log->Message("Warning: Could not create ocean object");
00078   } else {
00079     Log::s_log->Message("Ocean created");
00080   }
00081 
00082   // This is the array used to draw the terrain
00083   // Note: This array interleaves vertex (3), normal (3), texture coord 0 (2), and texture coord 1 (2) data
00084   m_terrainArray = new GLfloat[m_w*m_h*(3+3+2+2)];
00085 
00086   if (Config::s_config->CheckVBO()) {
00087     // Using VBO's, so terrainArray will get uploaded to the card and deleted from system memory
00088     // We need a copy of the vertex data for collisions
00089     m_vertexData = new GLfloat[m_w*m_h];
00090 
00091     // Set the vertexStride to be 1, since the data is tightly packed and there we only store the height
00092     m_vertexStride = 1;
00093 
00094     // Set vertexOffset to be 0, since all we are storing is the height values
00095     m_vertexOffset = 0;
00096   } else {
00097     // Using VA's so the terrainArray will stay valid
00098     m_vertexData = m_terrainArray;
00099 
00100     // Set the vertexStride to be (3+3+2+2), since terrainArray contains texture and normal data too.
00101     m_vertexStride = (3+3+2+2);
00102 
00103     // Set the vertexOffset to be 1, since the height is the second value in the array at a coord
00104     m_vertexOffset = 1;
00105   }
00106 
00107   p = (Uint8 *) Image->pixels;
00108 
00109   for (int z = 0; z < m_h; z++){
00110     for (int x = 0; x < m_w; x++){
00111       TERRAIN_ARRAY(x, z, ARRAY_VERTEX+0) = (GLfloat) x - xDiff;
00112       TERRAIN_ARRAY(x, z, ARRAY_VERTEX+1) = (p[z*Image->pitch + x*Image->format->BytesPerPixel] - 128) * vScaleIn;
00113       TERRAIN_ARRAY(x, z, ARRAY_VERTEX+2) = (GLfloat) z - zDiff;
00114 
00115       // If using VBO's construct the vertexData array, too
00116       // Note: This is needed because terrainArray will get uploaded to the card and deleted
00117       if (Config::s_config->CheckVBO()) {
00118         VERTEX(x, z) = TERRAIN_ARRAY(x, z, ARRAY_VERTEX+1);
00119       }
00120 
00121       // Color texture coords
00122       TERRAIN_ARRAY(x, z, ARRAY_TEX0+0) = ((float) x) / ((float) m_w);
00123       TERRAIN_ARRAY(x, z, ARRAY_TEX0+1) = ((float) z) / ((float) m_h);
00124 
00125       // Detail texture coords
00126       TERRAIN_ARRAY(x, z, ARRAY_TEX1+0) = x*DETAIL_SCALE;
00127       TERRAIN_ARRAY(x, z, ARRAY_TEX1+1) = z*DETAIL_SCALE;
00128 
00129       // The normals get calculated when calcNormals() is called
00130     }
00131   }
00132 
00133   SDL_FreeSurface(Image);
00134 
00135   Log::s_log->Message("Heightmap generated");
00136 
00137   CalcNormals();
00138   //CalcNormals2("./normalMap.png");
00139 
00140   Log::s_log->Message("Normals generated");
00141 
00142   //printf("%f %f %f\n", NORMALS(350, 350, 0), NORMALS(350, 350, 1), NORMALS(350, 350, 2));
00143 
00144   m_texture = new Texture("land.png");
00145   m_detailTex = new Texture("detail.png");
00146 
00147   // Set up opengl vertex arrays
00148   SetGlArrayPointers();
00149 
00150   // Set up the color texture
00151   glActiveTexture(GL_TEXTURE0);
00152   glEnable(GL_TEXTURE_2D);
00153   m_texture->BindTexture();
00154   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
00155 
00156   // Set up the color detail
00157   glActiveTexture(GL_TEXTURE1);
00158   glEnable(GL_TEXTURE_2D);
00159   m_detailTex->BindTexture();
00160   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
00161   glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE, 2);
00162 
00163   // Set up the material
00164   m_material[0] = 1.0;
00165   m_material[1] = 1.0;
00166   m_material[2] = 1.0;
00167   m_material[3] = 1.0;
00168 
00169   // Set up the root quadtree box
00170   box = new BoundBox();
00171 
00172   box->m_b1 = new Point(0.0 - (int) m_w/2, 0.0, 0.0 - (int) m_h/2);
00173   box->m_b2 = new Point((int) m_w/2, 0.0, 0.0 - (int) m_h/2);
00174   box->m_b3 = new Point(0.0 - (int) m_w/2, 0.0, (int) m_h/2 - 1);
00175   box->m_b4 = new Point((int) m_w/2 - 1, 0.0, (int) m_h/2 - 1);
00176 
00177   // Set up the frustum
00178   m_frustum = new Frustum();
00179 
00180   // Initialize the quadtree root
00181   m_root = new QuadNode(this, box);
00182 
00183   Log::s_log->Message("Quadtree generated");
00184 
00185   // Don't need to worry about deleting the box, the quadtree will do it
00186 
00187   if (Config::s_config->CheckVBO()) {
00188     // This data is now on the card, we don't need it anymore
00189     delete []m_terrainArray;
00190     m_terrainArray = NULL;
00191   }
00192 
00193   // Disable the multi-texturing, so other constructors aren't weird
00194   //glActiveTexture(GL_TEXTURE1);  // Should already be selected
00195   glDisable(GL_TEXTURE_2D);
00196   glActiveTexture(GL_TEXTURE0);
00197 }
00198 
00199 Terrain::~Terrain() {
00200   // Delete the ocean
00201   delete m_ocean;
00202 
00203   // Delete the quadtree
00204   delete m_root;
00205 
00206   // Delete the view frustum
00207   delete m_frustum;
00208 
00209   if (Config::s_config->CheckVBO()) {
00210     // Free the memory on the graphics card
00211     glDeleteBuffers(1, &m_buffer);
00212 
00213     // Delete the temp data used for collisions
00214     delete []m_vertexData;
00215   } else {
00216     // Vertex arrays were used, so delete all the data
00217     delete []m_terrainArray;
00218     m_terrainArray = NULL;
00219 
00220     // vertexData was set to terrainArray, so set it to NULL
00221     m_vertexData = NULL;
00222   }
00223 
00224   // Delete the textures
00225   delete m_texture;
00226   delete m_detailTex;
00227 
00228   // Again, still don't need to delete the constructor's box, the quadtree
00229   //   handles it
00230 
00231   Log::s_log->Message("Terrain deleted");
00232 }
00233 
00234 void Terrain::Update(unsigned int ticks) {
00235   m_ocean->Update(ticks);
00236 }
00237 
00238 void Terrain::Draw() {
00239   // Use the material
00240   glMaterialfv(GL_FRONT, GL_AMBIENT, m_material);
00241   glMaterialfv(GL_FRONT, GL_DIFFUSE, m_material);
00242 
00243   // Set up the color texture
00244   glActiveTexture(GL_TEXTURE0);
00245   //glEnable(GL_TEXTURE_2D);
00246   m_texture->BindTexture();
00247 
00248   // Set up the color detail
00249   glActiveTexture(GL_TEXTURE1);
00250   glEnable(GL_TEXTURE_2D);
00251   m_detailTex->BindTexture();
00252 
00253   m_frustum->GetFrustum();
00254 
00255   m_root->Draw(m_frustum);
00256 
00257   // Disable the texturing
00258   //glActiveTexture(GL_TEXTURE1);  // Should already be selected
00259   glDisable(GL_TEXTURE_2D);
00260   glActiveTexture(GL_TEXTURE0);
00261 
00262   glEnable(GL_BLEND);
00263   m_ocean->Draw();
00264   glDisable(GL_BLEND);
00265 }
00266 
00267 bool Terrain::CheckLineCollision(GLfloat xIn, GLfloat zIn, int size,
00268          double v[3], float mag) {
00269   int xMin, xMax;
00270   int zMin, zMax;
00271 
00272   xIn = (m_w/2) + xIn;
00273   zIn = (m_h/2) + zIn;
00274 
00275   xMin = (int) xIn - (size/2);
00276   xMax = (int) xIn + (size/2);
00277   if (xMin < 0) xMin = 0;
00278   if (xMax > (m_w-2)) xMax = (m_w-2);
00279 
00280   zMin = (int) zIn - (size/2);
00281   zMax = (int) zIn + (size/2);
00282   if (zMin < 0) zMin = 0;
00283   if (zMax > (m_h-2)) zMax = (m_h-2);
00284 
00285   // For some reason you need to do this to get collisions to work...
00286   v[2] = v[2] * -1;
00287 
00288   for (int z = zMin; z < zMax; z++){
00289     for(int x = xMin; x < xMax; x++) {
00290       if (NeedCollision(x, z)) {
00291         double orig[3];
00292         double vert0[3];
00293         double vert1[3];
00294         double vert2[3];
00295         double vert3[3];
00296         double r1, r2, r3;
00297 
00298         orig[0] = xIn;
00299         orig[1] = 0;
00300         orig[2] = zIn;
00301 
00302         vert0[0] = x;
00303         vert0[1] = VERTEX(x, z);
00304         vert0[2] = z;
00305 
00306         vert1[0] = x+1;
00307         vert1[1] = VERTEX(x+1, z);
00308         vert1[2] = z;
00309 
00310         vert2[0] = x;
00311         vert2[1] = VERTEX(x, z+1);
00312         vert2[2] = z+1;
00313 
00314         vert3[0] = x+1;
00315         vert3[1] = VERTEX(x+1, z+1);
00316         vert3[2] = z+1;
00317 
00318         if (intersect_triangle(orig, v, vert0, vert2, vert1, &r1, &r2, &r3)) {
00319           if ((r1 >= 0) && (r1 < mag)) {
00320             //Log::s_log->Message("Collision1: (%d, %d) %f < %f", x, z, r1, mag);
00321             return true;
00322           }
00323         }
00324         if (intersect_triangle(orig, v, vert2, vert3, vert1, &r1, &r2, &r3)) {
00325           if ((r1 >= 0) && (r1 < mag)) {
00326             //Log::s_log->Message("Collision2: (%d, %d) %f < %f", x, z, r1, mag);
00327             return true;
00328           }
00329         }
00330       }
00331     }
00332   }
00333 
00334   return false;
00335 }
00336 
00337 bool Terrain::NeedCollision(int x, int z) {
00338   GLfloat v1, v2, v3, v4;
00339 
00340   v1 = VERTEX(x, z);
00341   v2 = VERTEX(x, z+1);
00342   v3 = VERTEX(x+1, z);
00343   v4 = VERTEX(x+1, z+1);
00344 
00345   if (((v1 <= 0) || (v2 <= 0) || (v3 <= 0) || (v4 <= 0)) &&
00346       ((v1 >= 0) || (v2 >= 0) || (v3 >= 0) || (v4 >= 0))) {
00347     return true;
00348   } else return false;
00349 }
00350 
00351 
00352 void Terrain::CalcNormals() {
00353   GLfloat point1[3], point2[3], point3[3];
00354   GLfloat *tnormals1;
00355   GLfloat *tnormals2;
00356   // For loops, so MSVC++ doesn't complain
00357   short int z, x;
00358 
00359   if (m_terrainArray == NULL) return;
00360 
00361   tnormals1 = new GLfloat[m_w*m_h*3];
00362   tnormals2 = new GLfloat[m_w*m_h*3];
00363 
00364   for (z = 0; z < m_h-1; z++) {
00365     for (x = 0; x < m_w-1; x++) {
00366       point1[0] = x;
00367       point1[1] = TERRAIN_ARRAY(x, z, ARRAY_VERTEX+1);
00368       point1[2] = z;
00369 
00370       point2[0] = x;
00371       point2[1] = TERRAIN_ARRAY(x, z+1, ARRAY_VERTEX+1);
00372       point2[2] = z+1;
00373 
00374       point3[0] = x+1;
00375       point3[1] = TERRAIN_ARRAY(x+1, z, ARRAY_VERTEX+1);
00376       point3[2] = z;
00377 
00378       crossProduct(point1, point2, point3, &tnormals1[TNORMALS_INDEX(x, z, 0)]);
00379 
00380       point1[0] = x;
00381       point1[1] = TERRAIN_ARRAY(x, z+1, ARRAY_VERTEX+1);
00382       point1[2] = z+1;
00383 
00384       point2[0] = x+1;
00385       point2[1] = TERRAIN_ARRAY(x+1, z+1, ARRAY_VERTEX+1);
00386       point2[2] = z+1;
00387 
00388       point3[0] = x+1;
00389       point3[1] = TERRAIN_ARRAY(x+1, z, ARRAY_VERTEX+1);
00390       point3[2] = z;
00391 
00392       crossProduct(point1, point2, point3, &tnormals2[TNORMALS_INDEX(x, z, 0)]);
00393     }
00394   }
00395 
00396   for (z = 1; z < m_h-1; z++) {
00397     for (x = 1; x < m_w-1; x++) {
00398       /*
00399       NORMALS(x, z, 0) = (tnormals2[TNORMALS_INDEX(x-1, z-1, 0)] + tnormals1[TNORMALS_INDEX(x-1, z, 0)] +
00400         tnormals2[TNORMALS_INDEX(x-1, z, 0)] + tnormals1[TNORMALS_INDEX(x, z-1, 0)] +
00401         tnormals2[TNORMALS_INDEX(x, z-1, 0)] + tnormals1[TNORMALS_INDEX(x, z, 0)]);
00402       NORMALS(x, z, 1) = (tnormals2[TNORMALS_INDEX(x-1, z-1, 1)] + tnormals1[TNORMALS_INDEX(x-1, z, 1)] +
00403         tnormals2[TNORMALS_INDEX(x-1, z, 1)] + tnormals1[TNORMALS_INDEX(x, z-1, 1)] +
00404         tnormals2[TNORMALS_INDEX(x, z-1, 1)] + tnormals1[TNORMALS_INDEX(x, z, 1)]);
00405       NORMALS(x, z, 2) = (tnormals2[TNORMALS_INDEX(x-1, z-1, 2)] + tnormals1[TNORMALS_INDEX(x-1, z, 2)] +
00406         tnormals2[TNORMALS_INDEX(x-1, z, 2)] + tnormals1[TNORMALS_INDEX(x, z-1, 2)] +
00407         tnormals2[TNORMALS_INDEX(x, z-1, 2)] + tnormals1[TNORMALS_INDEX(x, z, 2)]);
00408       */
00409 
00410       TERRAIN_ARRAY(x, z, ARRAY_NORMAL+0) = (tnormals2[TNORMALS_INDEX(x-1, z-1, 0)] + tnormals1[TNORMALS_INDEX(x-1, z, 0)] +
00411         tnormals2[TNORMALS_INDEX(x-1, z, 0)] + tnormals1[TNORMALS_INDEX(x, z-1, 0)] +
00412         tnormals2[TNORMALS_INDEX(x, z-1, 0)] + tnormals1[TNORMALS_INDEX(x, z, 0)]);
00413       TERRAIN_ARRAY(x, z, ARRAY_NORMAL+1) = (tnormals2[TNORMALS_INDEX(x-1, z-1, 1)] + tnormals1[TNORMALS_INDEX(x-1, z, 1)] +
00414         tnormals2[TNORMALS_INDEX(x-1, z, 1)] + tnormals1[TNORMALS_INDEX(x, z-1, 1)] +
00415         tnormals2[TNORMALS_INDEX(x, z-1, 1)] + tnormals1[TNORMALS_INDEX(x, z, 1)]);
00416       TERRAIN_ARRAY(x, z, ARRAY_NORMAL+2) = (tnormals2[TNORMALS_INDEX(x-1, z-1, 2)] + tnormals1[TNORMALS_INDEX(x-1, z, 2)] +
00417         tnormals2[TNORMALS_INDEX(x-1, z, 2)] + tnormals1[TNORMALS_INDEX(x, z-1, 2)] +
00418         tnormals2[TNORMALS_INDEX(x, z-1, 2)] + tnormals1[TNORMALS_INDEX(x, z, 2)]);
00419 
00420       // Send normalize pointer to first element for that normal
00421       //normalize(&NORMALS(x, z, 0));
00422       normalize(&TERRAIN_ARRAY(x, z, ARRAY_NORMAL));
00423     }
00424   }
00425 
00426   // Get rid of temp normal arrays
00427   delete []tnormals1;
00428   delete []tnormals2;
00429 }
00430 
00431 GLfloat Terrain::GetHeight(int x, int y) {
00432   if ((x < m_w) && (y < m_h)) {
00433     return VERTEX(x, y);
00434   } else return 0;  // should throw an exception
00435 }
00436 
00437 // Note: The array that is returned by this function needs to be deleted by the caller
00438 unsigned int *Terrain::GenIndexArray(BoundBox *box, int *vertexCount, GLfloat *minHeight, GLfloat *maxHeight, unsigned int *minRange, unsigned int *maxRange) {
00439   // Used to find the min/max height in this quad box
00440   GLfloat tMaxHeight = -256;
00441   GLfloat tMinHeight = 256;
00442 
00443   unsigned int tMinRange = m_w*m_h+1;
00444   unsigned int tMaxRange = 0;
00445 
00446   // Used to go from centered coords to array indicies
00447   int xDiff = (int) m_w / 2;
00448   int zDiff = (int) m_h / 2;
00449 
00450   // Find min/max x coords in 0 to w range (rather than -w/2 to w/2)
00451   int xMin = ((int) box->m_b1->m_x) + xDiff;
00452   int xMax = ((int) box->m_b2->m_x) + xDiff;
00453 
00454   // Find min/max z coords in 0 to h range (rather than -h/2 to h/2)
00455   int zMin = ((int) box->m_b1->m_z) + zDiff;
00456   int zMax = ((int) box->m_b3->m_z) + zDiff;
00457 
00458   // These are used to allow generation of a single triangle strip, it works one direction, then back the other until the tile is drawn
00459   int step = -1;
00460   int xEnd;
00461   int x;
00462 
00463   int count = 0;
00464 
00465 
00466   // These are used to compute the number of vertices in this index array
00467   int xCount;
00468   int zCount;
00469 
00470   // Set the xCount and zCount
00471   xCount = (int) box->m_b2->m_x - (int) box->m_b1->m_x + 1;
00472   if (zMax >= m_h) {
00473     // The last row won't get drawn (z+1 would be out of bounds)
00474     zCount = (int) box->m_b3->m_z - (int) box->m_b1->m_z - 1;
00475   } else {
00476     // Need to account for the last row
00477     zCount = (int) box->m_b3->m_z - (int) box->m_b1->m_z;
00478   }
00479 
00480   // Compute the actual number of verticies
00481   *vertexCount = (xCount * 2 + 1) * zCount;
00482 
00483   unsigned int *indexArray = new unsigned int[*vertexCount];
00484 
00485   //cout << zMin-zDiff << " " << zMax-zDiff << endl;
00486 
00487   // Generate the actual index array
00488   for (int z = zMin; z < zMax; z++) {
00489     // The step allows generation of a single triangle strip, we need to work across one, then back the next...
00490     step = -1 * step;
00491     if (step > 0) {
00492       x = xMin;
00493       xEnd = xMax+1;
00494     } else {
00495       x = xMax;
00496       xEnd = xMin-1;
00497     }
00498 
00499     // Make sure z+1 is in bounds
00500     if (z+1 < m_h) {
00501       // It is, so loop through and draw this section of the strip
00502       while (x != xEnd) {
00503         // Check to see if we need to update min/maxHeight
00504         if (GetHeight(x, z) > tMaxHeight) tMaxHeight = GetHeight(x, z);
00505         if (GetHeight(x, z) < tMinHeight) tMinHeight = GetHeight(x, z);
00506 
00507         if (GetHeight(x, z+1) > tMaxHeight) tMaxHeight = GetHeight(x, z+1);
00508         if (GetHeight(x, z+1) < tMinHeight) tMinHeight = GetHeight(x, z+1);
00509 
00510         // Acutally add these two points, incrementing count as you insert
00511         indexArray[count++] = TERRAIN_ARRAY_INDEX(x, z);
00512         indexArray[count++] = TERRAIN_ARRAY_INDEX(x, z+1);
00513 
00514         if ((unsigned int) TERRAIN_ARRAY_INDEX(x, z) > tMaxRange) tMaxRange = TERRAIN_ARRAY_INDEX(x, z);
00515         if ((unsigned int) TERRAIN_ARRAY_INDEX(x, z+1) > tMaxRange) tMaxRange = TERRAIN_ARRAY_INDEX(x, z+1);
00516 
00517         if ((unsigned int) TERRAIN_ARRAY_INDEX(x, z) < tMinRange) tMinRange = TERRAIN_ARRAY_INDEX(x, z);
00518         if ((unsigned int) TERRAIN_ARRAY_INDEX(x, z+1) < tMinRange) tMinRange = TERRAIN_ARRAY_INDEX(x, z+1);
00519 
00520         // Increment (or decrement) x
00521         x += step;
00522       }
00523 
00524       // Add an extra point so that even with degenerate triangles opengl faces the polys the right direction
00525       indexArray[count++] = TERRAIN_ARRAY_INDEX(x-step, z+1);
00526     }
00527   }
00528 
00529   // Set the max/min to the values we found
00530   *maxHeight = tMaxHeight;
00531   *minHeight = tMinHeight;
00532 
00533   // Set the range to the values we found
00534   *minRange = tMinRange;
00535   *maxRange = tMaxRange;
00536 
00537   return indexArray;
00538 }
00539 
00540 void Terrain::SetGlArrayPointers() {
00541   if (Config::s_config->CheckVBO()) {
00542     // Using VBO's
00543 
00544     // Generate the buffer object
00545     glGenBuffers(1, &m_buffer);
00546 
00547     // Enable all of the arrays
00548     glEnableClientState(GL_VERTEX_ARRAY);
00549     glEnableClientState(GL_NORMAL_ARRAY);
00550     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00551     glClientActiveTexture(GL_TEXTURE1);
00552     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00553 
00554     // Bind the buffer object and copy the data to the video card
00555     glBindBuffer(GL_ARRAY_BUFFER_ARB, m_buffer);
00556     glBufferData(GL_ARRAY_BUFFER_ARB, m_w*m_h*(3+3+2+2)*sizeof(GL_FLOAT), m_terrainArray, GL_STATIC_DRAW_ARB);
00557 
00558     // Ugly hack to get offsets instead of pointers
00559     char *t = NULL;
00560 
00561     // Location of vertex data in the array
00562     glVertexPointer(3, GL_FLOAT, ARRAY_STRIDE, &t[ARRAY_VERTEX*sizeof(GL_FLOAT)]);
00563 
00564     // Location of normal data
00565     glNormalPointer(GL_FLOAT, ARRAY_STRIDE, &t[ARRAY_NORMAL*sizeof(GL_FLOAT)]);
00566 
00567     // Enable TEXTURE0 and give its location in the array
00568     glClientActiveTexture(GL_TEXTURE0);
00569     glTexCoordPointer(2, GL_FLOAT, ARRAY_STRIDE, &t[ARRAY_TEX0*sizeof(GL_FLOAT)]);
00570 
00571     // Enable TEXTURE1 and give its locatoin
00572     glClientActiveTexture(GL_TEXTURE1);
00573     glTexCoordPointer(2, GL_FLOAT, ARRAY_STRIDE, &t[ARRAY_TEX1*sizeof(GL_FLOAT)]);
00574   } else {
00575     // Using VA's
00576 
00577     // Enable arrays and give data locations
00578     glEnableClientState(GL_VERTEX_ARRAY);
00579     glVertexPointer(3, GL_FLOAT, ARRAY_STRIDE, &TERRAIN_ARRAY(0, 0, ARRAY_VERTEX));
00580 
00581     glEnableClientState(GL_NORMAL_ARRAY);
00582     glNormalPointer(GL_FLOAT, ARRAY_STRIDE, &TERRAIN_ARRAY(0, 0, ARRAY_NORMAL));
00583 
00584     glClientActiveTexture(GL_TEXTURE0);
00585     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00586     glTexCoordPointer(2, GL_FLOAT, ARRAY_STRIDE, &TERRAIN_ARRAY(0, 0, ARRAY_TEX0));
00587 
00588     glClientActiveTexture(GL_TEXTURE1);
00589     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00590     glTexCoordPointer(2, GL_FLOAT, ARRAY_STRIDE, &TERRAIN_ARRAY(0, 0, ARRAY_TEX1));
00591   }
00592 }
00593 
00594 void Terrain::GetMinMax(BoundBox *box, GLfloat *minHeight, GLfloat *maxHeight) {
00595   // High and low values for this chunk of terrain
00596   GLfloat tHigh = 0;
00597   GLfloat tLow = 0;
00598 
00599   // To center the terrain
00600   int xDiff = (int) m_w / 2;
00601   int zDiff = (int) m_h / 2;
00602 
00603   // For for loops
00604   int startX = ((int) box->m_b1->m_x) + xDiff;
00605   int endX = ((int) box->m_b2->m_x) + xDiff;
00606   int startZ = ((int) box->m_b1->m_z) + zDiff;
00607   int endZ = ((int) box->m_b3->m_z) + zDiff;
00608 
00609     // Step through the terrain to find high and low points
00610   for (int z = startZ; z < endZ; z++) {
00611     for (int x = startX; x < endX; x++) {
00612       //if (TERRAIN(x, z) > tHigh) tHigh = TERRAIN(x, z);
00613       //if (TERRAIN(x, z) < tLow) tLow = TERRAIN(x, z);
00614       if (VERTEX(x, z) > tHigh) tHigh = VERTEX(x, z);
00615       if (VERTEX(x, z) < tLow) tLow = VERTEX(x, z);
00616     }
00617   }
00618 
00619   // Finish up
00620   *maxHeight = tHigh;
00621   *minHeight = tLow;
00622 }
00623 
00624 GLfloat Terrain::CalcHeight(GLfloat x, GLfloat z) {
00625   // The idea here is that I cast a ray down from above the terrain using the
00626   //   collision checking code to figure out the exact height at any point
00627   int tempXCoord, tempZCoord;
00628 
00629   int xCoord = (int) x;
00630   int zCoord = (int) z;
00631 
00632   if ((x >= m_w) || (z >= m_h)) {
00633     // Should throw exception
00634     Log::s_log->Message("Warning: x (%d) or z (%d) too large in calcHeight", x, z);
00635     return 0;
00636   }
00637   if ((x < 0) || (z < 0)) {
00638     // Should throw exception
00639     Log::s_log->Message("Warning: x (%d) or z (%d) less than zero in calcHeight", x, z);
00640     return 0;
00641   }
00642 
00643   if (xCoord + 1 < m_w) {
00644     tempXCoord = xCoord + 1;
00645   } else {
00646     tempXCoord = xCoord;
00647   }
00648 
00649   if (zCoord + 1 < m_h) {
00650     tempZCoord = zCoord + 1;
00651   } else {
00652     tempZCoord = zCoord;
00653   }
00654 
00655   double orig[3] = {x, 128, z};  // Point we're casting from
00656   double ray[3] = {0, -1, 0};  // Direction of ray
00657   double vert0[3] = {xCoord, VERTEX(xCoord, zCoord), zCoord};
00658   double vert1[3] = {xCoord+1, VERTEX(tempXCoord, zCoord), zCoord};
00659   double vert2[3] = {xCoord, VERTEX(xCoord, tempZCoord), zCoord+1};
00660   double vert3[3] = {xCoord+1, VERTEX(tempXCoord, tempZCoord), zCoord+1};
00661   double r1, r2, r3;
00662 
00663   if (intersect_triangle(orig, ray, vert0, vert2, vert1, &r1, &r2, &r3)) {
00664     return 128 - r1;
00665   }
00666   if (intersect_triangle(orig, ray, vert2, vert3, vert1, &r1, &r2, &r3)) {
00667     return 128 - r1;
00668   }
00669 
00670   // Shouldn't ever get here because the ray should hit at least one of the triangles
00671   // Probably should throw an exception
00672   cout << "Problem!" << endl;
00673   return 0;
00674 }
00675 
00676 int Terrain::GetWidth() {
00677   return m_w;
00678 }
00679 
00680 int Terrain::GetHeight() {
00681   return m_h;
00682 }
00683 
00684 void Terrain::CalcNormals2(std::string file) {
00685   SDL_Surface *Image;
00686   Uint8 *p;
00687 
00688   // Load the image, check for errors, if image is not found quit
00689   Image = IMG_Load(file.c_str());
00690   if (!Image) {
00691     throw string("Could not load normals from " + file);
00692   }
00693 
00694   // Make sure same size image
00695   if ((Image->w != m_w) || (Image->h != m_h)) {
00696     throw string("Normal map and heightmap not the same size");
00697   }
00698 
00699 
00700   if (m_terrainArray == NULL) return;
00701 
00702   p = (Uint8 *) Image->pixels;
00703 
00704   for (int y = 0; y < Image->h; y++){
00705     for (int x = 0; x < Image->w; x++){
00706       //NORMALS(x, y, 0) = ((double) p[(y*Image->w + x)*Image->format->BytesPerPixel + 2*y] / 255) * 2 - 1;
00707       //NORMALS(x, y, 1) = ((double) p[(y*Image->w + x)*Image->format->BytesPerPixel + 2*y + 1] / 255) * 2 - 1;
00708       //NORMALS(x, y, 2) = ((double) p[(y*Image->w + x)*Image->format->BytesPerPixel + 2*y + 2] / 255) * 2 - 1;
00709 
00710       TERRAIN_ARRAY(x, y, ARRAY_NORMAL+0) = ((double) p[(y*Image->w + x)*Image->format->BytesPerPixel + 2*y] / 255) * 2 - 1;
00711       TERRAIN_ARRAY(x, y, ARRAY_NORMAL+1) = ((double) p[(y*Image->w + x)*Image->format->BytesPerPixel + 2*y + 1] / 255) * 2 - 1;
00712       TERRAIN_ARRAY(x, y, ARRAY_NORMAL+2) = ((double) p[(y*Image->w + x)*Image->format->BytesPerPixel + 2*y + 2] / 255) * 2 - 1;
00713       //printf("%f\n", ((double) p[(y*Image->w + x)*Image->format->BytesPerPixel + 2*y] / 255) * 2 - 1);
00714 
00715       // Send normalize pointer to first element for that normal
00716       //normalize(&NORMALS(x, y, 0));
00717       normalize(&TERRAIN_ARRAY(x, y, ARRAY_NORMAL));
00718     }
00719   }
00720 
00721   // Get rid of temp normal arrays
00722   SDL_FreeSurface(Image);
00723 }
00724 
00725 /* The idea of this function is that it figures out where all points that are
00726    on the shore and in the bounding box are.  Each wave emitter calls this on
00727    its box to get its list of points.
00728  */
00729 void Terrain::LocateShoreLine(BoundBox *box, list< Point* > *pointList) {
00730   bool insert;
00731 
00732   // To center the terrain
00733   int xDiff = (int) m_w / 2;
00734   int zDiff = (int) m_h / 2;
00735 
00736   // For for loops
00737   int startX = ((int) box->m_b1->m_x) + xDiff;
00738   int endX = ((int) box->m_b2->m_x) + xDiff;
00739   int startZ = ((int) box->m_b1->m_z) + zDiff;
00740   int endZ = ((int) box->m_b3->m_z) + zDiff;
00741 
00742   if (startX < 1) startX = 1;
00743   if (endX > m_w-2) endX = m_w-2;
00744   if (startZ < 1) startZ = 1;
00745   if (endZ > m_h-2) endZ = m_h-2;
00746 
00747   // Step through the points in the box, looking for the shore
00748   for (int z = startZ; z < endZ; z++) {
00749     for (int x = startX; x < endX; x++) {
00750 
00751       // Reset insert variable
00752       insert = false;
00753 
00754       // Make sure it's in the water, no sense in checking on land
00755       if (TERRAIN_ARRAY(x, z, ARRAY_VERTEX+1) > 0) continue;
00756 
00757       if (TERRAIN_ARRAY(x, z, ARRAY_VERTEX+1) == 0) insert = true;
00758 
00759       if (TERRAIN_ARRAY(x+1, z, ARRAY_VERTEX+1) > 0) insert = true;
00760       else if (TERRAIN_ARRAY(x-1, z, ARRAY_VERTEX+1) > 0) insert = true;
00761       else if (TERRAIN_ARRAY(x, z+1, ARRAY_VERTEX+1) > 0) insert = true;
00762       else if (TERRAIN_ARRAY(x, z-1, ARRAY_VERTEX+1) > 0) insert = true;
00763 
00764       // Don't check diagonally, it gives weird right angles
00765 
00766       if (insert) {
00767         pointList->push_back(new Point(x-xDiff, 0, z-zDiff));
00768       }
00769     }
00770   }
00771 }
00772 
00773 bool Terrain::FindNearestShoreAngle(Point *p, float *angle) {
00774   int dX = m_w/2;
00775   int dZ = m_h/2;
00776 
00777   int pX = (int)p->m_x+dX;
00778   int pZ = (int)p->m_z+dZ;
00779 
00780   double orig[3] = {pX, 0, pZ};  // Point we're casting from
00781   double ray[3];  // Direction of ray
00782   double vert0[3];
00783   double vert1[3];
00784   double vert2[3];
00785   double vert3[3];
00786   double r1, r2, r3;
00787 
00788   int minAngle = 0;
00789   double minDX;
00790   double minDZ;
00791   double minDist = 100.0;
00792 
00793   double tempAngleRadians;
00794 
00795   float height = CalcHeight(pX, pZ);
00796   if (height > -.0001) return false;
00797 
00798   // Cast a ray in all 360 degrees, see which one hits the land closest
00799   for (int tempAngle = 0; tempAngle < 360; tempAngle++) {
00800     tempAngleRadians = degreesToRadians(tempAngle);
00801     ray[0] = sin(tempAngleRadians);
00802     ray[1] = 0;
00803     ray[2] = cos(tempAngleRadians);
00804 
00805     // Only check within 1 in each direction, should be closer than that
00806     for (int z = pZ-1; z < pZ+1; z++) {
00807       for (int x = pX-1; x < pX+1; x++) {
00808         vert0[0] = x;
00809         vert0[1] = TERRAIN_ARRAY(x, z, ARRAY_VERTEX+1);
00810         vert0[2] = z;
00811 
00812         vert1[0] = x+1;
00813         vert1[1] = TERRAIN_ARRAY(x+1, z, ARRAY_VERTEX+1);
00814         vert1[2] = z;
00815 
00816         vert2[0] = x;
00817         vert2[1] = TERRAIN_ARRAY(x, z+1, ARRAY_VERTEX+1);
00818         vert2[2] = z+1;
00819 
00820         vert3[0] = x+1;
00821         vert3[1] = TERRAIN_ARRAY(x+1, z+1, ARRAY_VERTEX+1);
00822         vert3[2] = z+1;
00823 
00824         if (intersect_triangle(orig, ray, vert0, vert2, vert1, &r1, &r2, &r3)) {
00825           if ((r1 < minDist) && (r1 > 0)) {
00826             minDist = r1;
00827             minAngle = tempAngle;
00828           }
00829         }
00830         if (intersect_triangle(orig, ray, vert2, vert3, vert1, &r1, &r2, &r3)) {
00831           if ((r1 < minDist) && (r1 > 0)) {
00832             minDist = r1;
00833             minAngle = tempAngle;
00834           }
00835         }
00836       }
00837     }
00838   }
00839 
00840   if (minDist == 100) return false;
00841 
00842   // Calculate the impact location
00843   tempAngleRadians = degreesToRadians((double) minAngle);
00844   minDX = sin(tempAngleRadians) * minDist;
00845   minDZ = cos(tempAngleRadians) * minDist;
00846 
00847   // Update the point to be the point of impact
00848   p->m_x += minDX;
00849   p->m_y = 0;
00850   p->m_z += minDZ;
00851 
00852   *angle = minAngle;
00853 
00854   return true;
00855 }
00856 

Generated on Mon Jan 8 22:34:13 2007 for CrownandCutlass by  doxygen 1.4.7