//////////////////////////////////////////////////////////////////////////////// // 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 #include #include PatchGrid::PatchGrid(HeightMap *hMap, int patchSize) : hMap_(hMap), lastPos_(-1, -2, -3), patchSize_(patchSize), simulationTime_(0.0f), drawnPatches_(0), patches_(0), width_(0), height_(0) { } PatchGrid::~PatchGrid() { delete [] patches_; } void PatchGrid::generate() { if (hMap_->getMapWidth() % patchSize_ != 0 || hMap_->getMapHeight() % patchSize_ != 0) { dialogMessage("Scorched3D", formatString( "Landscape dimensions must be a factor of %i", patchSize_)); } delete [] patches_; width_ = (hMap_->getMapWidth()+1) / patchSize_; height_ = (hMap_->getMapHeight()+1) / patchSize_; coord_.generate(hMap_); patches_ = new Patch*[width_ * height_]; for (int y=0; y SimulationTimeStep) { simulationTime_ -= SimulationTimeStep; // Recalculate the first un-calculated variance Patch **patch = patches_; int totalChanged = 0; int patchTotal = width_ * height_; for (int p=0; pgetRecalculate()) { (*patch)->getRecalculate() = false; (*patch)->computeVariance(); if (++totalChanged >= 3) break; } patch++; } } } void PatchGrid::reset(ProgressCounter *counter) { DIALOG_ASSERT(patches_); if (counter) counter->setNewOp("Patch Grid"); // Totaly recalculate all patch grid variances Patch **patch = patches_; int totalPatches = width_ * height_; for (int p=0; pgetRecalculate() = false; (*patch)->computeVariance(); if (counter) counter->setNewPercentage( 100.0f * float(p) / float(totalPatches)); patch++; } // Recalculate all the variances due to tanks positions recalculateTankVariance(); // Make sure the next frame has everything reset // and recalculated lastPos_ = Vector(-1, -2, -3); } void PatchGrid::recalculateTankVariance() { if (!patches_) return; // Mark all patches as normal (non-tank) variance levels Patch **patch = patches_; for (int p=0; pgetForceVariance() = false; patch++; } // Mark any patches with tanks on as better variance levels // Ensure that any patches with tanks on them are drawn with // a very low varaince std::map &targets = ScorchedClient::instance()->getTargetContainer().getTargets(); std::map::iterator itor; for (itor = targets.begin(); itor != targets.end(); itor++) { Target *current = (*itor).second; if (current->getAlive()) { float posX = current->getTargetPosition()[0]; float posY = current->getTargetPosition()[1]; posX /= float(patchSize_); posY /= float(patchSize_); int posXI = (int) posX; int posYI = (int) posY; if (posXI < 0) posXI = 0; else if (posXI >= width_) posXI = width_ - 1; if (posYI < 0) posYI = 0; else if (posYI >= height_) posYI = height_ - 1; patches_[posXI + posYI * width_]->getForceVariance() = true; } } } void PatchGrid::recalculate(int posX, int posY, int dist) { DIALOG_ASSERT(patches_); // Make sure the next frame has everything reset // and recalculated lastPos_ = Vector(-1, -2, -3); // Tag the any patches that may have changed // so their variances are checked Patch **patch = patches_; for (int p=0; pgetX() - posX); int distY = abs(current->getY() - posY); if (distX < dist + current->getWidth() && distY < dist + current->getWidth()) { current->getRecalculate() = true; } patch++; } // Recalculate all the variances due to tanks positions recalculateTankVariance(); } void PatchGrid::tesselate() { if (OptionsDisplay::instance()->getNoTessalation()) return; // Only need to recalculate patches if the viewing camera has changed // position Vector &pos = GLCamera::getCurrentCamera()->getCurrentPos(); if (fabs(pos[0] - lastPos_[0]) < 1.0f && fabs(pos[1] - lastPos_[1]) < 1.0f && fabs(pos[2] - lastPos_[2]) < 1.0f) { return; } lastPos_ = pos; // Link all the patches together. TriNodePool::instance()->reset(); Patch **patch = patches_; for (int y=0; yreset(); if ( x < width_-1 ) (*patch)->getLeftTri()->LeftNeighbor = (*(patch+1))->getRightTri(); else (*patch)->getLeftTri()->LeftNeighbor = 0; if ( x > 0 ) (*patch)->getRightTri()->LeftNeighbor = (*(patch-1))->getLeftTri(); else (*patch)->getRightTri()->LeftNeighbor = 0; if ( y > 0 ) (*patch)->getLeftTri()->RightNeighbor = (*(patch-width_))->getRightTri(); else (*patch)->getLeftTri()->RightNeighbor = 0; if ( y < height_-1 ) (*patch)->getRightTri()->RightNeighbor = (*(patch+width_))->getLeftTri(); else (*patch)->getRightTri()->RightNeighbor = 0; patch++; } } // Tessalate Vector point; patch = patches_; for (int p=0; pgetVisible()) { point = (*patch)->getMidPoint(); point[2] = float(hMap_->getHeight((int) point[0], (int) point[1])); point -= pos; // Ramp is stored in a factor of 10 i.e. devide by 10 float distance = point.Magnitude(); float start = float(OptionsDisplay::instance()->getRoamVarianceStart()); float ramp = distance * float(OptionsDisplay::instance()->getRoamVarianceRamp()) / 10.0f; float tank = 0.0f; if ((*patch)->getForceVariance()) { tank = (float) OptionsDisplay::instance()->getRoamVarianceTank(); } float fvariance = start + ramp - tank; if (fvariance < start) fvariance = start; unsigned variance = unsigned(fvariance); (*patch)->tessalate(variance); } patch++; } } void PatchGrid::visibility() { GLCameraFrustum *frustum = GLCameraFrustum::instance(); int divideWidth = hMap_->getMapWidth() / hMap_->getMapMinWidth(); int divideHeight = hMap_->getMapHeight() / hMap_->getMapMinHeight(); // Check visibility Vector point, point2; Patch **patch = patches_; for (int y=0; ygetMidPoint(); point2 = (*patch)->getMidPoint(); point2[2] = MAX( hMap_->getMaxHeight(((*patch)->getX() + 2) / divideWidth, ((*patch)->getY() + 2) / divideHeight), hMap_->getMaxHeight(((*patch)->getX() + (*patch)->getWidth() - 2) / divideWidth, ((*patch)->getY() + (*patch)->getWidth() - 2) / divideHeight )) / 2.0f; point[2] = MAX( hMap_->getMaxHeight(((*patch)->getX() + 2) / divideWidth, ((*patch)->getY() + 2) / divideHeight), hMap_->getMaxHeight(((*patch)->getX() + (*patch)->getWidth() - 2) / divideWidth, ((*patch)->getY() + (*patch)->getWidth() - 2) / divideHeight )) / 2.0f; float width = MAX(point[2], (*patch)->getWidth() + 5.0f); bool pt1 = frustum->sphereInFrustum(point, width, GLCameraFrustum::FrustrumGreen); bool pt2 = frustum->sphereInFrustum(point2, width, GLCameraFrustum::FrustrumGreen); (*patch)->getVisible() = (pt1 || pt2); patch++; } } } void PatchGrid::draw(PatchSide::DrawType sides) { DIALOG_ASSERT(patches_); visibility(); tesselate(); glColor3f(1.0f, 1.0f, 1.0f); switch(sides) { case PatchSide::typeTop: { drawnPatches_ = 0; Patch **patch = patches_; for (int p=0; pgetVisible()) { drawnPatches_ ++; (*patch)->draw(sides); } patch++; } GLDynamicVertexArray::instance()->drawROAM(); } break; case PatchSide::typeNormals: { glNormal3f(0.0f, 0.0f, 1.0f); glBegin(GL_LINES); Patch **patch = patches_; for (int p=0; pgetVisible()) { (*patch++)->draw(sides); } } glEnd(); } break; } }