00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <land/LandVisibilityPatch.h>
00022 #include <land/VisibilityPatchGrid.h>
00023 #include <landscapemap/LandscapeMaps.h>
00024 #include <landscapemap/GraphicalHeightMap.h>
00025 #include <landscape/GraphicalLandscapeMap.h>
00026 #include <graph/OptionsDisplay.h>
00027 #include <graph/MainCamera.h>
00028 #include <client/ScorchedClient.h>
00029 #include <common/Logger.h>
00030 #include <GLEXT/GLStateExtension.h>
00031 #include <GLEXT/GLInfo.h>
00032 #include <GLW/GLWFont.h>
00033
00034 LandVisibilityPatch::LandVisibilityPatch() :
00035 visible_(false), recalculateErrors_(false),
00036 leftPatch_(0), rightPatch_(0),
00037 topPatch_(0), bottomPatch_(0),
00038 visibilityIndex_(-1),
00039 dataSize_(0)
00040 {
00041 }
00042
00043 LandVisibilityPatch::~LandVisibilityPatch()
00044 {
00045 }
00046
00047 static float getHeight(int x, int y)
00048 {
00049 int mapWidth = ScorchedClient::instance()->getLandscapeMaps().
00050 getGroundMaps().getLandscapeWidth();
00051
00052 GraphicalLandscapeMap *landscapeMap = (GraphicalLandscapeMap *)
00053 ScorchedClient::instance()->getLandscapeMaps().
00054 getGroundMaps().getHeightMap().getGraphicalMap();
00055 return landscapeMap->getHeightData()[x + (y * (mapWidth + 1))].floatPosition[2];
00056 }
00057
00058 static float calculateError(int x1, int x2, int y1, int y2,
00059 float x1y1, float x2y2, float x1y2, float x2y1)
00060 {
00061 if (x2 - x1 <= 1) return 0.0f;
00062
00063 int midx = (x1 + x2) / 2;
00064 int midy = (y1 + y2) / 2;
00065 float actualheight = getHeight(midx, midy);
00066
00067 float approxheight1 = (x1y1 + x2y2) / 2.0f;
00068 float approxheight2 = (x1y2 + x2y1) / 2.0f;
00069 float approxheight3 = (x1y1 + x1y2) / 2.0f;
00070 float approxheight4 = (x1y1 + x2y1) / 2.0f;
00071 float approxheight5 = (x1y2 + x2y2) / 2.0f;
00072 float approxheight6 = (x2y1 + x2y2) / 2.0f;
00073
00074 float heightdiff1 = fabs(approxheight1 - actualheight);
00075 float heightdiff2 = fabs(approxheight2 - actualheight);
00076
00077 float errorChild1 = calculateError(x1, midx, y1, midy,
00078 x1y1, approxheight1, approxheight3, approxheight4);
00079 float errorChild2 = calculateError(midx, x2, y1, midy,
00080 approxheight4, approxheight6, approxheight1, x2y1);
00081 float errorChild3 = calculateError(x1, midx, midy, y2,
00082 approxheight3, approxheight5, x1y2, approxheight2);
00083 float errorChild4 = calculateError(midx, x2, midy, y2,
00084 approxheight2, x2y2, approxheight5, approxheight6);
00085
00086 float errorChildren = MAX(errorChild1, MAX(errorChild2, MAX(errorChild3, errorChild4)));
00087 float totalError = MAX(errorChildren, MAX(heightdiff1, heightdiff2));
00088 return totalError;
00089 }
00090
00091 void LandVisibilityPatch::setLocation(int x, int y,
00092 LandVisibilityPatch *leftPatch,
00093 LandVisibilityPatch *rightPatch,
00094 LandVisibilityPatch *topPatch,
00095 LandVisibilityPatch *bottomPatch)
00096 {
00097 int mapWidth = ScorchedClient::instance()->getLandscapeMaps().
00098 getGroundMaps().getLandscapeWidth();
00099 int mapHeight = ScorchedClient::instance()->getLandscapeMaps().
00100 getGroundMaps().getLandscapeHeight();
00101
00102 DIALOG_ASSERT(x >= 0 && y >= 0 &&
00103 x < mapWidth && y < mapHeight);
00104
00105
00106 x_ = x; y_ = y;
00107 leftPatch_ = leftPatch;
00108 rightPatch_ = rightPatch;
00109 topPatch_ = topPatch;
00110 bottomPatch_ = bottomPatch;
00111
00112
00113 dataSize_ = (mapWidth + 1) * (mapHeight + 1);
00114
00115 GraphicalLandscapeMap *landscapeMap = (GraphicalLandscapeMap *)
00116 ScorchedClient::instance()->getLandscapeMaps().
00117 getGroundMaps().getHeightMap().getGraphicalMap();
00118 dataOffSet_ = (x + (y * (mapWidth + 1))) *
00119 sizeof(GraphicalLandscapeMap::HeightData) / sizeof(float);
00120
00121 calculateErrors();
00122 }
00123
00124 void LandVisibilityPatch::calculateErrors()
00125 {
00126 maxHeight_ = 0.0f;
00127 minHeight_ = 100000.0f;
00128 for (int i=0; i<=5; i++)
00129 {
00130 float error = 0.0f;
00131 if (i>0)
00132 {
00133 int skip = 1 << i;
00134 for (int y1=y_; y1<y_+32; y1+=skip)
00135 {
00136 for (int x1=x_; x1<x_+32; x1+=skip)
00137 {
00138 int x2 = x1 + skip;
00139 int y2 = y1 + skip;
00140
00141 float x1y1 = getHeight(x1, y1);
00142 float x2y2 = getHeight(x2, y2);
00143 float x1y2 = getHeight(x1, y2);
00144 float x2y1 = getHeight(x2, y1);
00145
00146 float thisError = calculateError(x1, x2, y1, y2,
00147 x1y1, x2y2, x1y2, x2y1);
00148 error = MAX(error, thisError);
00149
00150 if (x1y1 > maxHeight_) maxHeight_ = x1y1;
00151 if (x1y1 < minHeight_) minHeight_ = x1y1;
00152 }
00153 }
00154 }
00155
00156 indexErrors_[i] = error;
00157 }
00158
00159 float heightRange = maxHeight_ - minHeight_;
00160 boundingSize_ = MAX(32.0f, heightRange) * 1.25f;
00161 position_ = Vector(float(x_ + 16), float(y_ + 16),
00162 heightRange / 2.0f + minHeight_);
00163 }
00164
00165 bool LandVisibilityPatch::setVisible(float distance)
00166 {
00167 visible_ = true;
00168
00169 if (recalculateErrors_)
00170 {
00171 calculateErrors();
00172 recalculateErrors_ = false;
00173 }
00174
00175 float landDetailLevelRamp = (float)
00176 OptionsDisplay::instance()->getLandDetailLevelRamp();
00177 float maxError = ((distance - 32.0f) / landDetailLevelRamp) + 1.0f;
00178 if (maxError < 1.0f) maxError = 1.0f;
00179 else if (maxError > 5.0f) maxError = 5.0f;
00180
00181 visibilityIndex_ = 0;
00182 if (!OptionsDisplay::instance()->getNoLandLOD())
00183 {
00184 for (int i=0; i<=5; i++)
00185 {
00186 if (indexErrors_[i] > maxError) break;
00187 visibilityIndex_ = i;
00188 }
00189 }
00190
00191 return true;
00192 }
00193
00194 void LandVisibilityPatch::setNotVisible()
00195 {
00196 visible_ = false;
00197 }
00198
00199 void LandVisibilityPatch::draw(MipMapPatchIndex &index, bool simple)
00200 {
00201 GraphicalLandscapeMap *landscapeMap = (GraphicalLandscapeMap *)
00202 ScorchedClient::instance()->getLandscapeMaps().
00203 getGroundMaps().getHeightMap().getGraphicalMap();
00204 float *heightMapData = &landscapeMap->getHeightData()->floatPosition[0];
00205
00206
00207 GLInfo::addNoTriangles(index.getSize() - 2);
00208
00209 if (!OptionsDisplay::instance()->getNoGLDrawElements() &&
00210 GLStateExtension::hasDrawRangeElements())
00211 {
00212
00213 float *data = 0;
00214 if (landscapeMap->getBufferObject())
00215 {
00216 data = (float*) NULL + dataOffSet_;
00217 }
00218 else
00219 {
00220 data = &heightMapData[dataOffSet_];
00221 }
00222
00223
00224 glVertexPointer(3, GL_FLOAT, sizeof(GraphicalLandscapeMap::HeightData), data);
00225
00226 if (!simple)
00227 {
00228
00229 glNormalPointer(GL_FLOAT, sizeof(GraphicalLandscapeMap::HeightData), data + 3);
00230
00231
00232 if (GLStateExtension::hasMultiTex())
00233 {
00234 glClientActiveTextureARB(GL_TEXTURE1_ARB);
00235 glTexCoordPointer(2, GL_FLOAT, sizeof(GraphicalLandscapeMap::HeightData), data + 6);
00236 if (GLStateExtension::getTextureUnits() > 2)
00237 {
00238 glClientActiveTextureARB(GL_TEXTURE2_ARB);
00239 glTexCoordPointer(2, GL_FLOAT, sizeof(GraphicalLandscapeMap::HeightData), data + 8);
00240 }
00241 }
00242 glClientActiveTextureARB(GL_TEXTURE0_ARB);
00243 glTexCoordPointer(2, GL_FLOAT, sizeof(GraphicalLandscapeMap::HeightData), data + 6);
00244 }
00245
00246
00247 unsigned short *indices = 0;
00248 if (index.getBufferOffSet() != -1)
00249 {
00250 indices = (unsigned short *) NULL + (index.getBufferOffSet() / sizeof(unsigned short));
00251 }
00252 else
00253 {
00254 indices = index.getIndices();
00255 }
00256
00257
00258 glDrawRangeElements(GL_TRIANGLE_STRIP,
00259 index.getMinIndex(),
00260 index.getMaxIndex(),
00261 index.getSize(),
00262 GL_UNSIGNED_SHORT,
00263 indices);
00264 DIALOG_ASSERT((index.getMaxIndex()-index.getMinIndex()+1) <
00265 GLStateExtension::getMaxElementVertices());
00266 DIALOG_ASSERT(index.getSize() <
00267 GLStateExtension::getMaxElementIndices());
00268 }
00269 else
00270 {
00271 glBegin(GL_TRIANGLE_STRIP);
00272 for (int i=0; i<index.getSize(); i++)
00273 {
00274 float *data = &heightMapData[dataOffSet_] +
00275 (sizeof(GraphicalLandscapeMap::HeightData) / 4 * index.getIndices()[i]);
00276 if (!simple)
00277 {
00278 glNormal3fv(data + 3);
00279 glTexCoord2fv(data + 6);
00280 if (GLStateExtension::hasMultiTex())
00281 {
00282 glMultiTexCoord2fvARB(GL_TEXTURE1_ARB, data + 6);
00283 if (GLStateExtension::getTextureUnits() > 2)
00284 {
00285 glMultiTexCoord2fvARB(GL_TEXTURE2_ARB, data + 8);
00286 }
00287 }
00288 }
00289
00290 glVertex3fv(data);
00291 }
00292 glEnd();
00293 }
00294 }
00295
00296 void LandVisibilityPatch::drawLODLevel(MipMapPatchIndex &index)
00297 {
00298 if (OptionsDisplay::instance()->getDrawLines()) glPolygonMode(GL_FRONT, GL_FILL);
00299
00300 GraphicalLandscapeMap *landscapeMap = (GraphicalLandscapeMap *)
00301 ScorchedClient::instance()->getLandscapeMaps().
00302 getGroundMaps().getHeightMap().getGraphicalMap();
00303 float *heightMapData = &landscapeMap->getHeightData()->floatPosition[0];
00304
00305 Vector red(1.0f, 0.0f, 0.0f);
00306 Vector yellow(1.0f, 1.0f, 0.0f);
00307 GLWFont::instance()->getGameFont()->drawBilboard(red, 1.0f, 3.0f,
00308 position_[0], position_[1], position_[2],
00309 S3D::formatStringBuffer("%i", visibilityIndex_));
00310
00311 if ((MainCamera::instance()->getCamera().getLookAt() - position_).Magnitude() < 20.0f)
00312 {
00313 for (int i=0; i<index.getSize(); i++)
00314 {
00315 float *data = &heightMapData[dataOffSet_] +
00316 (sizeof(GraphicalLandscapeMap::HeightData) / 4 * index.getIndices()[i]);
00317
00318 GLWFont::instance()->getGameFont()->drawBilboard(yellow, 1.0f, 1.0f,
00319 data[0], data[1], data[2] + i * 1.5f,
00320 S3D::formatStringBuffer("%i", i));
00321 }
00322 }
00323
00324 if (OptionsDisplay::instance()->getDrawLines()) glPolygonMode(GL_FRONT, GL_LINE);
00325 }