00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <math.h>
00022 #include <stdlib.h>
00023 #include <landscapemap/HeightMapModifier.h>
00024 #include <landscapedef/LandscapeDefn.h>
00025 #include <common/Defines.h>
00026 #include <image/ImageBitmap.h>
00027 #include <image/ImageFactory.h>
00028 #include <lang/LangResource.h>
00029
00030 void HeightMapModifier::levelSurround(HeightMap &hmap)
00031 {
00032 for (int x=0; x<=hmap.getMapWidth(); x++)
00033 {
00034 hmap.setHeight(x, 0, fixed(0));
00035 hmap.setHeight(x, 1, fixed(0));
00036 hmap.setHeight(x, hmap.getMapHeight(), fixed(0));
00037 hmap.setHeight(x, hmap.getMapHeight()-1, fixed(0));
00038 }
00039
00040 for (int y=0; y<=hmap.getMapHeight(); y++)
00041 {
00042 hmap.setHeight(0, y, fixed(0));
00043 hmap.setHeight(1, y, fixed(0));
00044 hmap.setHeight(hmap.getMapWidth(), y, fixed(0));
00045 hmap.setHeight(hmap.getMapWidth()-1, y, fixed(0));
00046 }
00047 }
00048
00049 static int noiseFn(int x, int y, int random)
00050 {
00051 int n = x + y * 57 + random * 131;
00052 n = (n<<13) ^ n;
00053 return ((n * (n * n * 15731 + 789221) +
00054 1376312589)&0x7fffffff);
00055 }
00056
00057 void HeightMapModifier::noise(HeightMap &hmap,
00058 LandscapeDefnHeightMapGenerate &defn,
00059 RandomGenerator &generator,
00060 ProgressCounter *counter)
00061 {
00062 if (defn.noisefactor == 0) return;
00063 if (counter) counter->setNewOp(LANG_RESOURCE("NOISE", "Noise"));
00064
00065 unsigned int randomU = generator.getRandUInt() % 5000;
00066 int random = (int) randomU;
00067
00068 fixed *noisemap = new fixed[defn.noisewidth * defn.noiseheight];
00069 for (int y=0; y<defn.noiseheight; y++)
00070 {
00071 for (int x=0; x<defn.noisewidth; x++)
00072 {
00073 int noise = (noiseFn(x, y, random) -
00074 (INT_MAX / 2)) / 100000;
00075 noisemap[x + y * defn.noisewidth] =
00076 fixed(true, noise) * defn.noisefactor;
00077 }
00078 }
00079
00080
00081 for (int x=0; x<=hmap.getMapWidth(); x++)
00082 {
00083 if (counter) counter->setNewPercentage((100.0f * float(x)) / float(hmap.getMapWidth()));
00084 for (int y=0; y<=hmap.getMapHeight(); y++)
00085 {
00086 fixed nx = fixed(defn.noisewidth) * fixed(x) / fixed(hmap.getMapWidth());
00087 fixed ny = fixed(defn.noiseheight) * fixed(y) / fixed(hmap.getMapHeight());
00088 if (nx >= defn.noisewidth) nx = defn.noisewidth - 1;
00089 if (ny >= defn.noiseheight) ny = defn.noiseheight - 1;
00090
00091 fixed height = hmap.getHeight(x, y);
00092 fixed newHeight = height +
00093 noisemap[nx.asInt() + ny.asInt() * defn.noisewidth];
00094 newHeight = MAX(newHeight, 0);
00095 hmap.setHeight(x, y, newHeight);
00096 }
00097 }
00098 }
00099
00100 void HeightMapModifier::edgeEnhance(HeightMap &hmap,
00101 LandscapeDefnHeightMapGenerate &defn,
00102 RandomGenerator &generator,
00103 ProgressCounter *counter)
00104 {
00105 if (counter) counter->setNewOp(LANG_RESOURCE("EDGE_ENHANCE", "Edge Enhance"));
00106
00107 fixed *newhMap_ = new fixed[(hmap.getMapWidth()+1) * (hmap.getMapHeight()+1)];
00108 fixed *point = newhMap_;
00109 fixed max, pixel;
00110 for (int y=0; y<=hmap.getMapHeight(); y++)
00111 {
00112 if (counter) counter->setNewPercentage((100.0f * float(y)) / float(hmap.getMapHeight()));
00113 for (int x=0; x<=hmap.getMapWidth(); x++, point++)
00114 {
00115 fixed height = hmap.getHeight(x, y);
00116 if (x>0 && y>0 && x<hmap.getMapWidth() && y<hmap.getMapHeight())
00117 {
00118 max = (hmap.getHeight(x + 1, y + 1) - hmap.getHeight(x - 1, y - 1)).abs();
00119
00120 pixel = (hmap.getHeight(x - 1, y + 1) - hmap.getHeight(x + 1, y - 1)).abs();
00121 if (pixel > max) max = pixel;
00122
00123 pixel = (hmap.getHeight(x - 1, y) - hmap.getHeight(x + 1, y)).abs();
00124 if (pixel > max) max = pixel;
00125
00126 pixel = (hmap.getHeight(x, y - 1) - hmap.getHeight(x, y + 1)).abs();
00127 if (pixel > max) max = pixel;
00128
00129 *point = MAX(height, max + height);
00130 }
00131 else
00132 {
00133 *point = height;
00134 }
00135 *point = MAX(*point, 0);
00136 }
00137 }
00138
00139 fixed *start = newhMap_;
00140 for (int y=0; y<=hmap.getMapHeight(); y++)
00141 {
00142 for (int x=0; x<=hmap.getMapWidth(); x++)
00143 {
00144 hmap.setHeight(x, y, *(start++));
00145 }
00146 }
00147 delete [] newhMap_;
00148 }
00149
00150 static void getPos(int pos, int &x, int &y, int dist)
00151 {
00152 switch (pos)
00153 {
00154 case 0:
00155 x = -dist;
00156 y = -dist;
00157 break;
00158 case 1:
00159 x = dist;
00160 y = dist;
00161 break;
00162 case 2:
00163 x = dist;
00164 y = -dist;
00165 break;
00166 case 3:
00167 x = -dist;
00168 y = dist;
00169 break;
00170 case 4:
00171 x = -dist;
00172 y = 0;
00173 break;
00174 case 5:
00175 x = dist;
00176 y = 0;
00177 break;
00178 case 6:
00179 x = 0;
00180 y = dist;
00181 break;
00182 case 7:
00183 x = 0;
00184 y = -dist;
00185 break;
00186 }
00187 }
00188
00189 void HeightMapModifier::waterErrosion(HeightMap &hmap,
00190 LandscapeDefnHeightMapGenerate &defn,
00191 RandomGenerator &generator,
00192 ProgressCounter *counter)
00193 {
00194 if (defn.errosions == 0) return;
00195 if (counter) counter->setNewOp(LANG_RESOURCE("WATER_ERROSION", "Water Errosion"));
00196
00197
00198
00199 fixed *newhMap = new fixed[(hmap.getMapWidth()+1) * (hmap.getMapHeight()+1)];
00200 fixed *start = newhMap;
00201 for (int y=0; y<=hmap.getMapHeight(); y++)
00202 {
00203 for (int x=0; x<=hmap.getMapWidth(); x++, start++)
00204 {
00205 *start = hmap.getHeight(x, y);
00206 }
00207 }
00208
00209
00210 int *seen = new int[(hmap.getMapWidth()+1) * (hmap.getMapHeight()+1)];
00211 memset(seen, 0, sizeof(int) * (hmap.getMapWidth()+1) * (hmap.getMapHeight()+1));
00212
00213
00214 for (int o=0; o<defn.errosions; o++)
00215 {
00216 if (counter) counter->setNewPercentage((100.0f * float(o)) / float(30));
00217
00218
00219 int x = generator.getRandUInt() % hmap.getMapWidth();
00220 int y = generator.getRandUInt() % hmap.getMapHeight();
00221
00222
00223 int startx = x;
00224 int starty = y;
00225 fixed startHeight = hmap.getHeight(x, y);
00226
00227
00228 int nx, ny;
00229 for (int i=0; i<500; i++)
00230 {
00231
00232 fixed minHeight = fixed::MAX_FIXED;
00233 int distFromStart = (x - startx) * (x - startx) +
00234 (y - starty) * (y - starty);
00235
00236
00237
00238 int minIndex = -1;
00239 for (int a=0; a<4; a++)
00240 {
00241 getPos(a, nx, ny, 1);
00242 if (x + nx >= 0 &&
00243 y + ny >= 0 &&
00244 x + nx <= hmap.getMapWidth() &&
00245 y + ny <= hmap.getMapHeight())
00246 {
00247 int newDistFromStart =
00248 (x + nx - startx) * (x + nx - startx) +
00249 (y + ny - starty) * (y + ny - starty);
00250 if (newDistFromStart > distFromStart)
00251 {
00252 fixed newHeight = newhMap[x + nx + (y + ny) * (hmap.getMapWidth()+1)];
00253 if (newHeight < minHeight)
00254 {
00255 minIndex = a;
00256 minHeight = newHeight;
00257 }
00258 }
00259 }
00260 }
00261
00262
00263 if (minIndex == -1 || seen[x + y * (hmap.getMapWidth()+1)] > defn.errosionlayering)
00264 {
00265 break;
00266 }
00267 seen[x + y * (hmap.getMapWidth()+1)]++;
00268
00269
00270 fixed lowered = fixed(i) * defn.errosionforce;
00271 lowered = MIN(lowered, defn.errosionmaxdepth);
00272 fixed currentheight = hmap.getHeight(x, y);
00273 startHeight = MIN(startHeight, currentheight);
00274 fixed newheight = MAX(startHeight - lowered, 0);
00275 hmap.setHeight(x, y, newheight);
00276
00277
00278 int size = 1;
00279 while (true)
00280 {
00281 fixed raised = fixed(size) * defn.errosionsurroundforce;
00282 for (int a=-size; a<=size; a++)
00283 {
00284 for (int b=-size; b<=size; b++)
00285 {
00286 if (a==-size || a==size || b==-size || b==size)
00287 {
00288 fixed newSurroundHeight = startHeight - lowered + raised + generator.getRandFixed() * 2;
00289 newSurroundHeight = MAX(newSurroundHeight, 0);
00290 fixed oldSurroundHeight = hmap.getHeight(x + a, y + b);
00291 if (oldSurroundHeight > newSurroundHeight)
00292 {
00293 hmap.setHeight(x + a , y + b, newSurroundHeight);
00294 }
00295 }
00296 }
00297 }
00298 size++;
00299 if (size > defn.errosionsurroundsize) break;
00300 }
00301
00302
00303 {
00304 getPos(minIndex, nx, ny, 1);
00305 x += nx;
00306 y += ny;
00307 }
00308 }
00309 }
00310
00311 delete [] newhMap;
00312 delete [] seen;
00313 }
00314
00315 void HeightMapModifier::smooth(HeightMap &hmap,
00316 LandscapeDefnHeightMapGenerate &defn,
00317 ProgressCounter *counter)
00318 {
00319 if (defn.landsmoothing == 0) return;
00320 if (counter) counter->setNewOp(LANG_RESOURCE("SMOOTING", "Smoothing"));
00321
00322 fixed *newhMap_ = new fixed[(hmap.getMapWidth()+1) * (hmap.getMapHeight()+1)];
00323
00324 fixed matrix[5][5];
00325 for (int i=0; i<5; i++)
00326 {
00327 for (int j=0; j<5; j++)
00328 {
00329 matrix[i][j] = fixed(defn.landsmoothing);
00330 if (i==2 && j==2) matrix[i][j] = fixed(1);
00331 }
00332 }
00333
00334 int x;
00335 for (x=0; x<=hmap.getMapWidth(); x++)
00336 {
00337 if (counter) counter->setNewPercentage((100.0f * float(x)) / float(hmap.getMapWidth()));
00338 for (int y=0; y<=hmap.getMapHeight(); y++)
00339 {
00340
00341 fixed total = fixed(0);
00342 fixed inc = fixed(0);
00343 for (int i=0; i<5; i++)
00344 {
00345 for (int j=0; j<5; j++)
00346 {
00347 int newi = i + x - 2;
00348 int newj = j + y - 2;
00349 if (newi>=0 && newi <= hmap.getMapWidth() &&
00350 newj>=0 && newj <= hmap.getMapHeight())
00351 {
00352 inc += matrix[i][j] * hmap.getHeight(newi, newj);
00353 total += matrix[i][j];
00354 }
00355 }
00356 }
00357
00358 newhMap_[(hmap.getMapWidth()+1) * y + x] = inc / total;
00359 }
00360 }
00361
00362 fixed *start = newhMap_;
00363 for (int y=0; y<=hmap.getMapHeight(); y++)
00364 {
00365 for (x=0; x<=hmap.getMapWidth(); x++)
00366 {
00367 hmap.setHeight(x, y, *(start++));
00368 }
00369 }
00370 delete [] newhMap_;
00371 }
00372
00373 void HeightMapModifier::scale(HeightMap &hmap,
00374 LandscapeDefnHeightMapGenerate &defn,
00375 RandomGenerator &generator,
00376 ProgressCounter *counter)
00377 {
00378 if (counter) counter->setNewOp(LANG_RESOURCE("SCALING_PHASE_1", "Scaling Phase 1"));
00379
00380 fixed max = hmap.getHeight(0,0);
00381 fixed min = hmap.getHeight(0,0);
00382
00383 int x;
00384 for (x=0; x<=hmap.getMapWidth(); x++)
00385 {
00386 if (counter) counter->setNewPercentage((100.0f * float(x)) / float(hmap.getMapWidth()));
00387 for (int y=0; y<=hmap.getMapHeight(); y++)
00388 {
00389 if (hmap.getHeight(x,y) > max) max = hmap.getHeight(x,y);
00390 if (hmap.getHeight(x,y) < min) min = hmap.getHeight(x,y);
00391 }
00392 }
00393
00394 if (counter) counter->setNewOp(LANG_RESOURCE("SCALING_PHASE_2", "Scaling Phase 2"));
00395
00396 fixed realMax = ((fixed(defn.landheightmax) - fixed(defn.landheightmin)) * generator.getRandFixed()) +
00397 defn.landheightmin;
00398 fixed per = realMax / max;
00399
00400 for (x=0; x<=hmap.getMapWidth(); x++)
00401 {
00402 if (counter) counter->setNewPercentage((100.0f * float(x)) / float(hmap.getMapWidth()));
00403 for (int y=0; y<=hmap.getMapHeight(); y++)
00404 {
00405 fixed height = hmap.getHeight(x,y);
00406 hmap.setHeight(x,y, height * per);
00407 }
00408 }
00409 }
00410
00411 void HeightMapModifier::addCirclePeak(HeightMap &hmap, FixedVector &start,
00412 fixed sizew, fixed sizew2, fixed sizeh,
00413 RandomGenerator &offsetGenerator)
00414 {
00415 fixed maxdist = MAX(sizew2, sizew);
00416 fixed sizewsq = sizew * sizew * fixed(true, 12000);
00417 int startx = MAX(0, (start[0] - maxdist).asInt());
00418 int starty = MAX(0, (start[1] - maxdist).asInt());
00419 int endx = MIN(hmap.getMapWidth(), (start[0] + maxdist).asInt());
00420 int endy = MIN(hmap.getMapHeight(), (start[1] + maxdist).asInt());
00421
00422 fixed posX, posY;
00423 fixed pt2 = fixed(1) / fixed(5);
00424 for (int x=startx; x<=endx; x++)
00425 {
00426 for (int y=starty; y<=endy; y++)
00427 {
00428 posX = fixed(x);
00429 posY = fixed(y);
00430
00431 fixed distX = (posX - start[0]) * sizew2 / sizew;
00432 fixed distY = posY - start[1];
00433 fixed distsq = distX * distX + distY * distY;
00434
00435 if (distsq < sizewsq)
00436 {
00437 fixed dist = distsq.sqrt();
00438 fixed distRand = (dist / fixed(20)) * pt2;
00439 if (distRand > pt2) distRand = pt2;
00440 dist *= offsetGenerator.getRandFixed() * distRand + fixed(1) - (distRand / fixed(2));
00441 if (dist < sizew)
00442 {
00443 fixed newHeight = (dist * fixed::XPI / sizew).cos() * sizeh / fixed(4) + sizeh / fixed(4);
00444 newHeight += hmap.getHeight(x,y);
00445 hmap.setHeight(x, y, newHeight);
00446 }
00447 }
00448 }
00449 }
00450 }
00451
00452 void HeightMapModifier::generateTerrain(HeightMap &hmap,
00453 LandscapeDefnHeightMapGenerate &defn,
00454 RandomGenerator &generator,
00455 RandomGenerator &offsetGenerator,
00456 ProgressCounter *counter)
00457 {
00458 if (counter) counter->setNewOp(LANG_RESOURCE("TERAFORM_LANDSCAPE", "Teraform Landscape"));
00459
00460
00461 ImageBitmap bmap(256, 256);
00462 ImageHandle maskMap = bmap;
00463
00464
00465 if (!defn.mask.empty())
00466 {
00467 std::string fileName = S3D::getDataFile(defn.mask.c_str());
00468 maskMap = ImageFactory::loadImageHandle(fileName);
00469 if (!maskMap.getBits())
00470 {
00471 S3D::dialogExit("Landscape", S3D::formatStringBuffer(
00472 "Error: Failed to find mask map \"%s\"",
00473 fileName.c_str()));
00474 }
00475 }
00476
00477
00478 hmap.reset();
00479 fixed useWidthX = fixed(hmap.getMapWidth());
00480 fixed useWidthY = fixed(hmap.getMapHeight());
00481 fixed useBorderX = (fixed(hmap.getMapWidth()) - useWidthX) / fixed(2);
00482 fixed useBorderY = (fixed(hmap.getMapHeight()) - useWidthY) / fixed(2);
00483 fixed maskMultX = fixed(maskMap.getWidth()) / fixed(hmap.getMapWidth());
00484 fixed maskMultY = fixed(maskMap.getHeight()) / fixed(hmap.getMapHeight());
00485
00486 const int noItter = (fixed(defn.landhillsmax - defn.landhillsmin) *
00487 generator.getRandFixed() + fixed(defn.landhillsmin)).asInt();
00488
00489 for (int i=0; i<noItter; i++)
00490 {
00491 if (counter) counter->setNewPercentage((100.0f * float(i)) / float(noItter));
00492
00493
00494 fixed sizew = (fixed(defn.landpeakwidthxmax) - fixed(defn.landpeakwidthxmin)) * generator.getRandFixed()
00495 + fixed(defn.landpeakwidthxmin);
00496 fixed sizew2 = (fixed(defn.landpeakwidthymax) - fixed(defn.landpeakwidthymin)) * generator.getRandFixed()
00497 + fixed(defn.landpeakwidthymin) + sizew;
00498 fixed sizeh = ((fixed(defn.landpeakheightmax) - fixed(defn.landpeakheightmin)) * generator.getRandFixed()
00499 + defn.landpeakheightmin) * MAX(sizew, sizew2);
00500
00501
00502 fixed bordersizex = fixed(0);
00503 fixed bordersizey = fixed(0);
00504 if (defn.levelsurround)
00505 {
00506 fixed bordersize = MAX(sizew, sizew2) * fixed(true, 12000);
00507 bordersizex = bordersize + useBorderX;
00508 bordersizey = bordersize + useBorderY;
00509 }
00510
00511
00512 fixed sx = (generator.getRandFixed() * (fixed(hmap.getMapWidth()) -
00513 (bordersizex * fixed(2)))) + bordersizex;
00514 fixed sy = (generator.getRandFixed() * (fixed(hmap.getMapHeight()) -
00515 (bordersizey * fixed(2)))) + bordersizey;
00516
00517
00518 bool ok = true;
00519 if (sx.asInt() >= 0 && sx.asInt() < hmap.getMapWidth() &&
00520 sy.asInt() >= 0 && sy.asInt() < hmap.getMapHeight())
00521 {
00522 int bx = (sx * maskMultX).asInt();
00523 int by = maskMap.getWidth() - (sy * maskMultY).asInt();
00524 if (bx >= 0 && bx < maskMap.getWidth() &&
00525 by >= 0 && by < maskMap.getHeight())
00526 {
00527 unsigned char maskPt = maskMap.getBits()[(bx * 3) + (by * maskMap.getWidth() * 3)];
00528
00529
00530 ok = ((generator.getRandFixed() * fixed(255)) < fixed(maskPt));
00531 }
00532 }
00533
00534 if (ok)
00535 {
00536
00537 FixedVector start(sx, sy, fixed(0));
00538 addCirclePeak(hmap, start, sizew, sizew2, sizeh, offsetGenerator);
00539 }
00540 }
00541
00542 scale(hmap, defn, generator, counter);
00543 noise(hmap, defn, generator, counter);
00544
00545 waterErrosion(hmap, defn, generator, counter);
00546 if (defn.levelsurround) levelSurround(hmap);
00547 smooth(hmap, defn, counter);
00548 if (defn.levelsurround) levelSurround(hmap);
00549 }
00550