//////////////////////////////////////////////////////////////////////////////// // Scorched3D (c) 2000-2003 // // This file is part of Scorched3D. // // Scorched3D 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. // // Scorched3D 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 Scorched3D; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #define SQRT_OF_TWO_INV 0.707106781188f #define CALC_FORCE_ENTRY(x) f = currentEntry->height - x.height; \ currentEntry->force -= f; x.force += f; #define CALC_FORCE_ENTRY_SQ(x) f = (currentEntry->height - x.height) \ * SQRT_OF_TWO_INV; currentEntry->force -= f; x.force += f; WaterMap::WaterMap() : heights_(0), visible_(0), drawNormals_(false), drawVisiblePoints_(false), waterTexture_(0) { } void WaterMap::generate(int width, int height, int startX, int startY, int mapMult, int squareWidth) { startX_ = (float) startX; startY_ = (float) startY; visibilitySquareSize_ = squareWidth; mapWidth_ = width; mapHeight_ = height; mapWidthMult_ = mapMult; mapHeightMult_ = mapMult; surround_.generate(mapWidth_, mapHeight_, startX, startY, mapWidthMult_, mapHeightMult_); // Create water entry structs delete [] heights_; heights_ = new WaterEntry[mapWidth_ * mapHeight_]; memset(heights_, 0, sizeof(WaterEntry) * mapWidth_ * mapHeight_); // Create visiblity grid delete [] visible_; noVisiblesWidth_ = mapWidth_ / visibilitySquareSize_; noVisiblesHeight_ = mapHeight_ / visibilitySquareSize_; visible_ = new VisibleEntry[noVisiblesWidth_ * noVisiblesHeight_]; // Set the visiblity grid positions and default visiblity for (int y=0; ynotVisible = &visible_[x + y * noVisiblesWidth_]. notVisible; currentEntry->texX = float(i) / float(mapWidth_); currentEntry->texY = float(j) / float(mapHeight_); float scale = 1.0f; if (i<4) scale = float(i) / 4.0f; else if (j<4) scale = float(j) / 4.0f; else if (i>mapWidth_ - 4) scale = float(i - (mapWidth_ - 4)) / 4.0f; else if (j>mapHeight_ - 4) scale = float(j - (mapHeight_ - 4)) / 4.0f; currentEntry->scaling = scale; currentEntry++; } } WaterMap::~WaterMap() { } WaterMap::WaterEntry &WaterMap::getNearestWaterPoint(Vector &point) { // Not efficient and should only be called during init float pointY = float(startY_); WaterEntry *currentEntry = heights_; WaterEntry *result = currentEntry; float dist = 1000.0f; for (int j=0; jheight); float currentDist = (pt-point).Magnitude(); if(currentDist < dist) { result = currentEntry; dist = currentDist; } currentEntry++; pointX += mapWidthMult_; } pointY += mapHeightMult_; } return *result; } void WaterMap::draw() { glColor3f(1.0f, 1.0f, 1.0f); if (drawNormals_) { GLState currentState(GLState::TEXTURE_OFF); drawNormals(); } glPushAttrib(GL_TEXTURE_BIT); if (GLStateExtension::glActiveTextureARB()) { GLStateExtension::glActiveTextureARB()(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); waterDetail_.draw(true); GLStateExtension::glActiveTextureARB()(GL_TEXTURE0_ARB); } if (OptionsDisplay::instance()->getNoGLSphereMap()) { GLState currentState(GLState::TEXTURE_ON | GLState::BLEND_ON); waterTexture_->draw(); // Set up texture coordinate generation for reflections float PlaneS[] = { 0.0f, 1.0f / 20.0f, 0.0f, 0.0f }; float PlaneT[] = { 1.0f / 20.0f, 0.0f, 0.0f, 0.0f }; glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGenfv(GL_S, GL_OBJECT_PLANE, PlaneS); glTexGenfv(GL_T, GL_OBJECT_PLANE, PlaneT); // Draw the water surround_.draw(); drawWater(); } else if (GLStateExtension::hasCubeMap()) { GLState currentState(GLState::TEXTURE_OFF | GLState::BLEND_ON | GLState::CUBEMAP_ON); // Set up texture coordinate generation for reflections glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT); glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT); glTexGenf(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT); // Draw the water surround_.draw(); drawWater(); } else { GLState currentState(GLState::TEXTURE_ON | GLState::BLEND_ON); waterTexture_->draw(); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGenf(GL_R, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // Draw the water surround_.draw(); drawWater(); } if (GLStateExtension::glActiveTextureARB()) { GLStateExtension::glActiveTextureARB()(GL_TEXTURE1_ARB); glDisable(GL_TEXTURE_2D); GLStateExtension::glActiveTextureARB()(GL_TEXTURE0_ARB); } glPopAttrib(); } void WaterMap::refreshVisiblity() { float width = float(visibilitySquareSize_ + visibilitySquareSize_ + visibilitySquareSize_ + visibilitySquareSize_); GLCameraFrustum *frustum = GLCameraFrustum::instance(); VisibleEntry *currentEntry = visible_; if (drawVisiblePoints_) { glColor3f(1.0f, 0.0f, 0.0f); glPointSize(10.0f); glBegin(GL_POINTS); } for (int y=0; ypos); currentEntry->notVisible = !frustum->sphereInFrustum( currentEntry->pos, width, GLCameraFrustum::FrustrumBlue); currentEntry ++; } if (drawVisiblePoints_) { glEnd(); glPointSize(1.0f); } } void WaterMap::drawNormals() { float pointY = float(startY_); WaterEntry *currentEntry = heights_; glBegin(GL_LINES); for (int j=0; jheight); glVertex3fv(pt); Vector normal( currentEntry->normal[2], currentEntry->normal[0], currentEntry->normal[1]); glVertex3fv(pt + normal * 2.0f); currentEntry++; pointX += mapWidthMult_; } pointY += mapHeightMult_; } glEnd(); } void WaterMap::drawWater() { if (OptionsDisplay::instance()->getNoWaterMovement()) { glBegin(GL_QUADS); glNormal3f(0.0f, 1.0f, 0.0f); glColor4f(0.7f, 0.7f, 0.7f, 0.9f); if (GLStateExtension::glMultiTextCoord2fARB()) { GLStateExtension::glMultiTextCoord2fARB() (GL_TEXTURE1_ARB, 0.0f, 0.0f); } glVertex3f(startX_, startY_, 0.0f); if (GLStateExtension::glMultiTextCoord2fARB()) { GLStateExtension::glMultiTextCoord2fARB() (GL_TEXTURE1_ARB, 32.0f, 0.0f); } glVertex3f(startX_ + mapWidthMult_ * (mapWidth_ - 1), startY_, 0.0f); if (GLStateExtension::glMultiTextCoord2fARB()) { GLStateExtension::glMultiTextCoord2fARB() (GL_TEXTURE1_ARB, 32.0f, 32.0f); } glVertex3f(startX_ + mapWidthMult_ * (mapWidth_ - 1), startY_ + mapHeightMult_ * (mapHeight_ - 1), 0.0f); if (GLStateExtension::glMultiTextCoord2fARB()) { GLStateExtension::glMultiTextCoord2fARB() (GL_TEXTURE1_ARB, 0.0f, 32.0f); } glVertex3f(startX_, startY_ + mapHeightMult_ * (mapHeight_ - 1), 0.0f); glEnd(); return; } refreshVisiblity(); float pointY = startY_; WaterEntry *currentEntry = heights_; for (int j=0; jnotVisible) thisDontDraw = currentEntry->dontDraw && otherEntry->dontDraw; if (thisDontDraw && dontDraw) { if (glbegin) { glEnd(); glbegin = false; } } else { if (!glbegin) { glBegin(GL_QUAD_STRIP); glbegin = true; }; float otherHeight = otherEntry->height + 0.0f; float height = currentEntry->height + 0.0f; // Make the heights at the side of water // The actual side height if (i==0 || i==mapWidth_-1) { otherHeight = 0.0f; height = 0.0f; } if (j==0) height = 0.0f; else if (j==mapHeight_-2) otherHeight = 0.0f; if (otherEntry->depth < 0.2f) { otherHeight = 0.0f; } if (currentEntry->depth < 0.2f) { height = 0.0f; } // Make the shallows a lighter color float otherColor = 0.7f; float otherAlpha = 0.9f; if (otherEntry->depth < 1.0f) { otherColor = 1.0f; otherAlpha = otherEntry->depth * 0.5f + 0.4f; } glColor4f(otherColor, otherColor, otherColor, otherAlpha); // Set the normal glNormal3f(otherEntry->normal[0], otherEntry->normal[1], otherEntry->normal[2]); // Set tex coord if (GLStateExtension::glMultiTextCoord2fARB()) { GLStateExtension::glMultiTextCoord2fARB() (GL_TEXTURE1_ARB, otherEntry->texX * 16.0f, otherEntry->texY * 16.0f); } // Draw pt glVertex3f(pointX, pointY + mapHeightMult_, otherHeight); // Make the shallows a lighter color float currentColor = 0.7f; float currentAlpha = 0.9f; if (currentEntry->depth < 1.0f) { currentColor = 1.0f; currentAlpha = currentEntry->depth * 0.5f + 0.4f; } glColor4f(currentColor, currentColor, currentColor, currentAlpha); // Set the normal glNormal3f(currentEntry->normal[0], currentEntry->normal[1], currentEntry->normal[2]); // Set tex coord if (GLStateExtension::glMultiTextCoord2fARB()) { GLStateExtension::glMultiTextCoord2fARB() (GL_TEXTURE1_ARB, currentEntry->texX * 16.0f, currentEntry->texY * 16.0f); } // Draw pt glVertex3f(pointX, pointY, height); GLInfo::addNoTriangles(2); } currentEntry++; pointX += mapWidthMult_; dontDraw = thisDontDraw; } if (glbegin) { glEnd(); glbegin = false; } pointY += mapHeightMult_; } } void WaterMap::simulate(float frameTime) { if (OptionsDisplay::instance()->getNoWaterMovement()) return; static float waveTime = 0.0f; waveTime += frameTime; // Hack code to get around differing simulation rates // on different machines // No matter how many simulation points a second we // still calculate the ripples 20 times a second if (waveTime > 1.0f) waveTime = 1.0f; while (waveTime >= 0.05f) { generateSplash(); calculateRipples(); waveTime -= 0.05f; } } void WaterMap::generateSplash() { // Generate random position withing the boundaries of the water surface int iWavePosX = (unsigned int) (rand() / (float) RAND_MAX * (mapWidth_ - 2)); int iWavePosY = (unsigned int) (rand() / (float) RAND_MAX * (mapHeight_ - 2)); addWave(iWavePosX, iWavePosY, 0.2f); } void WaterMap::addWave(int iWavePosX, int iWavePosY, float height) { if (OptionsDisplay::instance()->getNoWaterMovement()) return; if (iWavePosX>0 && iWavePosY>0 && iWavePosXheight = height; (currentEntry+1)->height = height; (currentEntry+mapWidth_)->height = height; (currentEntry+mapWidth_+1)->height = height; } } void WaterMap::calculateRipples() { float f; Vector crossA, crossB; WaterEntry *currentEntry = heights_; const int widthPlusOne = mapWidth_ + 1; const int widthMinusOne = mapWidth_ - 1; int i,j; for (j=0; jdontDraw && i > 0 && j > 0 && i < mapWidth_-1 && j < mapHeight_-1) { // Load values from array WaterEntry ¤tEntryD = *(currentEntry - mapWidth_); WaterEntry ¤tEntryU = *(currentEntry + mapWidth_); WaterEntry ¤tEntryR = *(currentEntry + 1); WaterEntry ¤tEntryRU = *(currentEntry + widthPlusOne); WaterEntry ¤tEntryDL = *(currentEntry - widthPlusOne); WaterEntry ¤tEntryRD = *(currentEntry + widthMinusOne); // Calculate new force values CALC_FORCE_ENTRY(currentEntryD); CALC_FORCE_ENTRY(currentEntryU); CALC_FORCE_ENTRY(currentEntryR); CALC_FORCE_ENTRY_SQ(currentEntryRU); CALC_FORCE_ENTRY_SQ(currentEntryDL); CALC_FORCE_ENTRY_SQ(currentEntryRD); // Calculate normal vectors crossA[0] = currentEntryU.height - currentEntry->height; crossA[1] = 0.0f; crossA[2] = (float) mapHeightMult_; crossB[0] = (float) mapWidthMult_; crossB[1] = currentEntryR.height - currentEntry->height; crossB[2] = 0.0f; // Calculate the normal crossA *= crossB; crossA.StoreNormalize(); currentEntry->normal = crossA; } else { // Calculate the normal currentEntry->normal[1] = 1.0f; } currentEntry++; } } currentEntry = heights_; for (j=0; jdontDraw) { // Change the height of the water with the new force values currentEntry->velocity += currentEntry->force * 0.003f; currentEntry->height += currentEntry->velocity; currentEntry->height -= currentEntry->height / float(mapWidth_ * mapHeight_); currentEntry->height *= currentEntry->scaling; if (currentEntry->height > 5.0f) currentEntry->height = 5.0; else if (currentEntry->height < -5.0f) currentEntry->height = -5.0f; currentEntry->force = 0.0f; } currentEntry++; } } void WaterMap::reset() { WaterEntry *currentEntry = heights_; for (int j=0; jheight = 0.0f; currentEntry->velocity = 0.0f; currentEntry->force = 0.0f; currentEntry++; } }