//////////////////////////////////////////////////////////////////////////////// // 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 WaterWaves::WaterWaves() : totalTime_(0.0f), waveDistance_(0) { } WaterWaves::~WaterWaves() { } void WaterWaves::generateWaves(float waterHeight, WaterMap &wmap, ProgressCounter *counter) { int mapWidth = ScorchedClient::instance()->getLandscapeMaps(). getGroundMaps().getMapWidth(); int mapHeight = ScorchedClient::instance()->getLandscapeMaps(). getGroundMaps().getMapHeight(); // Wave points WaterWaveContext context; context.pointCount = 0; context.removedCount = 0; context.pointsMult = 1; context.mapWidth = mapWidth; context.mapHeight = mapHeight; context.pointsWidth = mapWidth / context.pointsMult; context.pointsHeight = mapHeight / context.pointsMult; context.wavePoints = new bool[context.pointsWidth * context.pointsHeight]; memset(context.wavePoints, 0, context.pointsWidth * context.pointsHeight * sizeof(bool)); // Wave distance distanceWidthMult_ = 4; distanceHeightMult_ = 4; distanceWidth_ = mapWidth / distanceWidthMult_; distanceHeight_ = mapHeight / distanceHeightMult_; delete [] waveDistance_; waveDistance_ = new float[distanceWidth_ * distanceHeight_]; for (int a=0; asetNewOp("Creating waves 1"); findPoints(&context, waterHeight, counter); // Do this after the findPoints so the waveDistance array is always completed if (OptionsDisplay::instance()->getNoWaves()) return; if (!OptionsDisplay::instance()->getDrawWater()) return; // Find the list of points that are next to eachother if (counter) counter->setNewOp("Creating waves 2"); while (findNextPath(&context, waterHeight, wmap, counter)) {} delete [] context.wavePoints; } void WaterWaves::findPoints(WaterWaveContext *context, float waterHeight, ProgressCounter *counter) { int distanceCount = 0; for (int y=context->pointsMult; ymapHeight - context->pointsMult; y+=context->pointsMult) { if (counter) counter->setNewPercentage(float(y)/ float(context->pointsHeight)*100.0f); for (int x=context->pointsMult; xmapWidth - context->pointsMult; x+=context->pointsMult) { float height = ScorchedClient::instance()->getLandscapeMaps(). getGroundMaps().getHeight(x, y); if (height > waterHeight - 4.0f && height < waterHeight) { float height2= ScorchedClient::instance()->getLandscapeMaps(). getGroundMaps().getHeight(x+context->pointsMult, y); float height3= ScorchedClient::instance()->getLandscapeMaps(). getGroundMaps().getHeight(x-context->pointsMult, y); float height4= ScorchedClient::instance()->getLandscapeMaps(). getGroundMaps().getHeight(x, y+context->pointsMult); float height5= ScorchedClient::instance()->getLandscapeMaps(). getGroundMaps().getHeight(x, y-context->pointsMult); if (height2 > waterHeight || height3 > waterHeight || height4 > waterHeight || height5 > waterHeight) { if (++distanceCount % 2 == 0) { Vector posA((float) x, (float) y, 0.0f); for (int b=0; bpointsWidth)), float(float(b) / float(distanceHeight_) * float(context->pointsHeight)), 0.0f); float distance = (posB - posA).Magnitude(); waveDistance_[a + b * distanceWidth_] = MIN(waveDistance_[a + b * distanceWidth_], distance); } } } { int a = x / context->pointsMult; int b = y / context->pointsMult; bool &pts = context->wavePoints[ a + b * context->pointsWidth]; if (!pts) { context->pointCount ++; pts = true; } } } } } } } float WaterWaves::getWaveDistance(int posx, int posy) { int a = int(posx) / distanceWidthMult_; int b = int(posy) / distanceHeightMult_; int x = MAX(0, MIN(a, (distanceWidth_ - 1))); int y = MAX(0, MIN(b, (distanceHeight_ - 1))); float distance = waveDistance_[x + y * distanceWidth_]; distance += fabsf(float(a - x)) + fabsf(float(b - y)); return distance; } bool WaterWaves::findNextPath(WaterWaveContext *context, float waterHeight, WaterMap &wmap, ProgressCounter *counter) { std::vector points; for (int y=1; ypointsHeight; y++) { for (int x=1; xpointsWidth; x++) { if (context->wavePoints[x + y * context->pointsWidth]) { findPath(context, points, x, y, counter); constructLines(context, waterHeight, wmap, points); return true; } } } return false; } void WaterWaves::findPath(WaterWaveContext *context, std::vector &points, int x, int y, ProgressCounter *counter) { if (x < 0 || x >= context->pointsWidth || y < 0 || y >= context->pointsHeight) { return; } context->removedCount ++; points.push_back( Vector( float(x * context->pointsMult), float(y * context->pointsMult), 0.0f)); if (counter && context->removedCount % 50 == 0) counter->setNewPercentage( float(context->removedCount)/float(context->pointCount)*100.0f); context->wavePoints[x + y * context->pointsWidth] = false; if (context->wavePoints[(x+1) + y * context->pointsWidth]) findPath(context, points, x+1, y, counter); else if (context->wavePoints[(x-1) + y * context->pointsWidth]) findPath(context, points, x-1, y, counter); else if (context->wavePoints[x + (y-1) * context->pointsWidth]) findPath(context, points, x, y-1, counter); else if (context->wavePoints[x + (y+1) * context->pointsWidth]) findPath(context, points, x, y+1, counter); else if (context->wavePoints[(x-1) + (y-1) * context->pointsWidth]) findPath(context, points, x-1, y-1, counter); else if (context->wavePoints[(x-1) + (y+1) * context->pointsWidth]) findPath(context, points, x-1, y+1, counter); else if (context->wavePoints[(x+1) + (y+1) * context->pointsWidth]) findPath(context, points, x+1, y+1, counter); else if (context->wavePoints[(x+1) + (y-1) * context->pointsWidth]) findPath(context, points, x+1, y-1, counter); } void WaterWaves::constructLines(WaterWaveContext *context, float waterHeight, WaterMap &wmap, std::vector &points) { Vector ptA; Vector ptB; Vector point = points.front(); int dist = int(RAND * 10.0f + 1.0f); for (int i=0; i<(int)points.size(); i++) { Vector ¤t = points[i]; int diffX = int(current[0]) - int(point[0]); int diffY = int(current[1]) - int(point[1]); int actualdist = diffX * diffX + diffY * diffY; if (actualdist >= dist) { WaterWaveEntry entry; Vector grad = point-current; Vector perp = grad.get2DPerp().Normalize2D(); int newx = int(current[0] + perp[0] * 3.0f); int newy = int(current[1] + perp[1] * 3.0f); if (newx <= 0 || newy <= 0 || newx >= context->mapWidth || newy >= context->mapHeight) { } else { if (ScorchedClient::instance()->getLandscapeMaps().getGroundMaps().getHeight( newx, newy) > waterHeight) { perp =- perp; ptA = current - perp / 3.0f; ptB = point - perp / 3.0f; entry.perp = perp; entry.ptC = point + perp * 6.0f; entry.ptD = current + perp * 6.0f; } else { ptA = point - perp / 3.0f; ptB = current - perp / 3.0f; entry.perp = perp; entry.ptC = current + perp * 6.0f; entry.ptD = point + perp * 6.0f; } entry.ptAEntry = &wmap.getNearestWaterPoint(ptA); entry.ptBEntry = &wmap.getNearestWaterPoint(ptB); entry.ptCEntry = &wmap.getNearestWaterPoint(entry.ptC); entry.ptDEntry = &wmap.getNearestWaterPoint(entry.ptD); if (RAND > 0.5f) paths1_.push_back(entry); else paths2_.push_back(entry); } if (i > 1) { i-= 1; point = points[i]; } else point = current; dist = int(RAND * 10.0f + 1.0f); } } } void WaterWaves::simulate(float frameTime) { totalTime_ += frameTime / 2.0f; if (totalTime_ > 6.0f) totalTime_ = 0.0f; } void WaterWaves::draw() { if (OptionsDisplay::instance()->getNoWaves()) return; Vector windDir = ScorchedClient::instance()->getOptionsTransient().getWindDirection(); Vector windDirPerp = windDir.Normalize(); GLState state(GLState::TEXTURE_ON | GLState::BLEND_ON); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glDepthMask(GL_FALSE); wavesTexture1_.draw(true); drawBoxes(totalTime_ + 0.0f, windDirPerp, paths1_); drawBoxes(totalTime_ + 2.0f, windDirPerp, paths1_); drawBoxes(totalTime_ + 4.0f, windDirPerp, paths1_); wavesTexture2_.draw(true); drawBoxes(totalTime_ + 1.0f, windDirPerp, paths2_); drawBoxes(totalTime_ + 3.0f, windDirPerp, paths2_); drawBoxes(totalTime_ + 5.0f, windDirPerp, paths2_); glDepthMask(GL_TRUE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } void WaterWaves::drawBoxes(float totalTime, Vector &windDirPerp, std::vector &paths) { float newTime = totalTime; if (newTime > 6.0f) newTime -= 6.0f; // First set of boxes // Some set of magic to try to get it look kind of ok! float alpha = 1.0f; float frontlen = newTime + 0.2f; float endlen = newTime / 2.0f; if (newTime < 1.0f) { alpha = newTime; } if (newTime > 4.0f) { frontlen = 4.2f - (newTime - 4.0f) / 3.0f; endlen = 2.0f - (newTime - 4.0f) / 3.0f; alpha = (2.0f - (newTime - 4.0f)) / 2.0f; } frontlen *= 2.0f; endlen *= 2.0f; // Draw the actual texture boxes glColor4f(wavesColor_[0], wavesColor_[1], wavesColor_[2], alpha * 0.3f); glBegin(GL_QUADS); Vector ptA, ptB, ptC, ptD; std::vector::iterator itor = paths.begin(); std::vector::iterator enditor = paths.end(); for (; itor != enditor; itor++) { WaterWaveEntry &p = *itor; if (((p.perp[0] * windDirPerp[0]) + (p.perp[1] * windDirPerp[1])) > 0.0f) continue; ptA = p.ptD - p.perp * frontlen; ptA[2] = p.ptAEntry->height + 0.05f; ptB = p.ptC - p.perp * frontlen; ptB[2] = p.ptBEntry->height + 0.05f; ptC = p.ptC - p.perp * endlen; ptC[2] = p.ptCEntry->height + 0.05f; ptD = p.ptD - p.perp * endlen; ptD[2] = p.ptCEntry->height + 0.05f; glTexCoord2f(1.0f, 1.0f); glVertex3fv(ptA); glTexCoord2f(0.0f, 1.0f); glVertex3fv(ptB); glTexCoord2f(0.0f, 0.0f); glVertex3fv(ptC); glTexCoord2f(1.0f, 0.0f); glVertex3fv(ptD); } glEnd(); GLInfo::addNoTriangles(paths.size() - 2); }