/* Copyright (C) 2001 W.P. van Paassen - peter@paassen.tmfweb.nl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "WP_Math.h" #include "WP_Vertex.h" #include "WP_Quad.h" #include "WP_TextureManager.h" #include "WP_Terrain.h" namespace WPCG { WP_Terrain::WP_Terrain(int width, int height, int number_iterations, scalar resolution, int scale): displayID(0) { //creates a random 3D terrain WP_Vertex **height_map = new WP_Vertex*[height]; if (!height_map) { cerr << "Unable to allocate memory for height_map" << endl; exit(-1); } int i; for (i = 0; i < height; i++) { height_map[i] = new WP_Vertex[width]; if (!height_map[i]) { cerr << "Unable to allocate memory for height_map" << endl; exit(-1); } } int k,l; //create heightmap for (i = 0; i < number_iterations; i++) { //create hill //choose random center in height_map int center_x = random() % width; int center_y = random() % height; int radius = random() % (width >> 1); int radius_square = radius << 1; //FIXME optimize, cycles through all vertices for (k = 0; k < height; k++) { for (l = 0; l < width; l++) { //raise hill int x = center_x - l; x *= x; int z = center_y - k; z *= z; float y = radius_square - (x + z); if (y > 0.0) { if (height_map[k][l].point.data[1] > 0.0) { height_map[k][l].point.data[1] += y; } else { height_map[k][l].point.data[1] = y; } } } } } //normalize terrain int min = INT_MAX; int max = 0; for (k = 0; k < height; k++) { for (l = 0; l < width; l++) { if (height_map[k][l].point.data[1] > max) { max = (int)height_map[k][l].point.data[1]; } else if (height_map[k][l].point.data[1] < min) { min = (int)height_map[k][l].point.data[1]; } } } scalar norm = max - min; if (norm == 0.0) { norm = 0.0001; } for (k = 0; k < height; k++) { for (l = 0; l < width; l++) { height_map[k][l].point.data[1] = (height_map[k][l].point.data[1] - min) / norm; } } //flatten terrain for (k = 0; k < height; k++) { for (l = 0; l < width; l++) { height_map[k][l].point.data[1] *= height_map[k][l].point.data[1]; } } //finalize height map (by adding x and z values and scaling by spread and resolution for (k = 0; k < height; k++) { for (l = 0; l < width; l++) { height_map[k][l].point.data[0] = k * resolution; height_map[k][l].point.data[1] *= scale; height_map[k][l].point.data[2] = l * resolution; } } //create quad list for vertex normal calculation (for per vertex lighting) list quads; for (k = 0; k < height - 1; k++) { for (l = 0; l < width - 1; l++) { WP_Quad* quad = new WP_Quad(); quad->vertices[0] = &height_map[k + 1][l + 1]; quad->vertices[1] = &height_map[k][l + 1]; quad->vertices[2] = &height_map[k][l]; quad->vertices[3] = &height_map[k + 1][l]; // compute normals WP_Vector3D v1 = quad->vertices[0]->point - quad->vertices[3]->point; WP_Vector3D v2 = quad->vertices[2]->point - quad->vertices[1]->point; if (!v1.crossProduct(v2)) { v1 = WP_Vector3D(1.0, 1.0, 1.0); } else { v1.normalize(); } quad->normal = v1; quads.push_back(quad); } } list::const_iterator q; // calculate normal for each individual vertex for (k = 0; k < height - 1; k++) { for (l = 0; l < width - 1; l++) { WP_Vector3D normal_sum(1.0, 1.0, 1.0); q = quads.begin(); WP_Vertex* v = &height_map[k][l]; while (q != quads.end()) { if ((*q)->vertices[0] == v || (*q)->vertices[1] == v || (*q)->vertices[2] == v || (*q)->vertices[3] == v) { normal_sum += (*q)->normal; } q++; } normal_sum.normalize(); // find vertex in height map q = quads.begin(); while (q != quads.end()) { if ((*q)->vertices[0] == v) { (*q)->vertices[0]->normal = normal_sum; break; } else if ((*q)->vertices[1] == v) { (*q)->vertices[1]->normal = normal_sum; break; } else if ((*q)->vertices[2] == v) { (*q)->vertices[2]->normal = normal_sum; break; } else if ((*q)->vertices[3] == v) { (*q)->vertices[3]->normal = normal_sum; break; } q++; } } } WP_TextureManager* tex_manager = WP_TextureManager::getInstance(); tex_manager->mipmapping = true; GLuint ft_id = tex_manager->getTexture("terrain.pcx", this); //create display list displayID = glGenLists(1); glNewList(displayID, GL_COMPILE); glPushMatrix(); WP_GLState::getInstance()->enableTexture2D(); glBindTexture(GL_TEXTURE_2D, ft_id); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, 1); //priority of current texture set high q = quads.begin(); while (q != quads.end()) { glBegin(GL_POLYGON); glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0.0, 0.0); glNormal3fv((*q)->vertices[3]->normal.data); glVertex3fv((*q)->vertices[3]->point.data); glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(0.0, 1.0); glNormal3fv((*q)->vertices[2]->normal.data); glVertex3fv((*q)->vertices[2]->point.data); glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(1.0, 1.0); glNormal3fv((*q)->vertices[1]->normal.data); glVertex3fv((*q)->vertices[1]->point.data); glColor4f(1.0, 1.0, 1.0, 1.0); glTexCoord2f(1.0, 0.0); glNormal3fv((*q)->vertices[0]->normal.data); glVertex3fv((*q)->vertices[0]->point.data); glEnd(); ++q; } WP_GLState::getInstance()->disableTexture2D(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, 0); //priority of last texture set low because it will not be used anymore glPopMatrix(); glEndList(); //clear quads q = quads.begin(); while (q != quads.end()) { delete (*q); q++; } for (i = 0; i < height; i++) { delete[] height_map[i]; } delete[] height_map; displacement_x = (width * resolution) / 2; displacement_y = (height * resolution) / 2; } WP_Terrain::~WP_Terrain() { if (displayID != 0) { glDeleteLists(displayID, 1); } } void WP_Terrain::drawTerrain() const { glPushMatrix(); glTranslatef(middle.data[0] - displacement_x, middle.data[1], middle.data[2] - displacement_y); glCallList(displayID); glPopMatrix(); } }