| 1 |
// |
| 2 |
// AutoCoast: Automatic Coastline scenery generator for Flight Simulator |
| 3 |
// |
| 4 |
// Copyright (c) 2004, Takuya Murakami. All rights reserved. |
| 5 |
// |
| 6 |
// Redistribution and use in source and binary forms, with or without |
| 7 |
// modification, are permitted provided that the following conditions |
| 8 |
// are met: |
| 9 |
// |
| 10 |
// 1. Redistributions of source code must retain the above copyright |
| 11 |
// notice, this list of conditions and the following disclaimer. |
| 12 |
// |
| 13 |
// 2. Redistributions in binary form must reproduce the above copyright |
| 14 |
// notice, this list of conditions and the following disclaimer in the |
| 15 |
// documentation and/or other materials provided with the distribution. |
| 16 |
// |
| 17 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 18 |
// ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 19 |
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 20 |
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR |
| 21 |
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| 22 |
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| 23 |
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| 24 |
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| 25 |
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| 26 |
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| 27 |
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 |
|
| 29 |
// cellarea.cc : Cell/Area process |
| 30 |
|
| 31 |
#include <math.h> |
| 32 |
#include <list> |
| 33 |
|
| 34 |
#include "vector.hpp" |
| 35 |
#include "cell.hpp" |
| 36 |
#include "bgl.hpp" |
| 37 |
#include "opt.hpp" |
| 38 |
|
| 39 |
/////////////////////////////////////////////////////////////////////// |
| 40 |
// Cell |
| 41 |
|
| 42 |
Cell::Cell(void) |
| 43 |
{ |
| 44 |
cellData = NULL; |
| 45 |
} |
| 46 |
|
| 47 |
Cell::~Cell() |
| 48 |
{ |
| 49 |
if (cellData) delete cellData; |
| 50 |
} |
| 51 |
|
| 52 |
// Convert coordinates |
| 53 |
double Cell::x2lon(int u) |
| 54 |
{ |
| 55 |
double lon = u * 360.0 / 768.0 - 180.0; |
| 56 |
if (lon < 0) lon += 360.0; // 0 to 360 |
| 57 |
|
| 58 |
return lon; |
| 59 |
} |
| 60 |
|
| 61 |
int Cell::lon2x(double lon) |
| 62 |
{ |
| 63 |
if (lon > 180) lon -= 360; // -180 to 180 |
| 64 |
return (int)((lon + 180.0) * 768.0 / 360.0); |
| 65 |
} |
| 66 |
|
| 67 |
double Cell::y2lat(int v) |
| 68 |
{ |
| 69 |
return 90.0 - v * 180.0 / 512.0; |
| 70 |
} |
| 71 |
|
| 72 |
int Cell::lat2y(double lat) |
| 73 |
{ |
| 74 |
return (int)((90.0 - lat) * 512.0 / 180.0); |
| 75 |
} |
| 76 |
|
| 77 |
void Cell::setid(int xx, int yy) |
| 78 |
{ |
| 79 |
x = xx; |
| 80 |
y = yy; |
| 81 |
|
| 82 |
bbox.xmin = x2lon(x); |
| 83 |
bbox.xmax = x2lon(x+1); |
| 84 |
if (bbox.xmax == 0.0) bbox.xmax = 360.0; |
| 85 |
|
| 86 |
bbox.ymax = y2lat(y); |
| 87 |
bbox.ymin = y2lat(y+1); |
| 88 |
|
| 89 |
for (int u = 0; u < 32; u++) { |
| 90 |
for (int v = 0; v < 32; v++) { |
| 91 |
area[u][v].setid(u, v, bbox); |
| 92 |
} |
| 93 |
} |
| 94 |
} |
| 95 |
|
| 96 |
bool Cell::Clipping(DataSet *source) |
| 97 |
{ |
| 98 |
if (cellData) { |
| 99 |
delete cellData; |
| 100 |
} |
| 101 |
|
| 102 |
cellData = source->Clipping(bbox); |
| 103 |
if (source->isEmpty(bbox)) { |
| 104 |
return false; |
| 105 |
} |
| 106 |
|
| 107 |
for (int u = 0; u < 32; u++) { |
| 108 |
for (int v = 0; v < 32; v++) { |
| 109 |
area[u][v].Clipping(cellData); |
| 110 |
area[u][v].setType(); |
| 111 |
} |
| 112 |
} |
| 113 |
|
| 114 |
return true; |
| 115 |
} |
| 116 |
|
| 117 |
void Cell::checkFill(LwmDataChunk *chunk, int u, int v, int blocksz) |
| 118 |
{ |
| 119 |
int uu, vv; |
| 120 |
int type, firsttype; |
| 121 |
|
| 122 |
if (area[u][v].mark) return; // already done... |
| 123 |
|
| 124 |
firsttype = area[u][v].getType(); // initial... |
| 125 |
if (isLand(firsttype)) { |
| 126 |
// AreaFill does not work for land area! |
| 127 |
// We must use land class or fill it with a polygon. |
| 128 |
return; |
| 129 |
} |
| 130 |
|
| 131 |
for (uu = u; uu < u + blocksz; uu++) { |
| 132 |
for (vv = v; vv < v + blocksz; vv++) { |
| 133 |
type = area[uu][vv].getType(); |
| 134 |
|
| 135 |
if (isLand(type)) return; // mix! |
| 136 |
} |
| 137 |
} |
| 138 |
|
| 139 |
// okay fill area... |
| 140 |
chunk->DataAreaFill(blocksz, 0, u, v); |
| 141 |
|
| 142 |
// set mark |
| 143 |
for (uu = u; uu < u + blocksz; uu++) { |
| 144 |
for (vv = v; vv < v + blocksz; vv++) { |
| 145 |
area[uu][vv].mark = true; |
| 146 |
} |
| 147 |
} |
| 148 |
} |
| 149 |
|
| 150 |
// |
| 151 |
// Generate LWM bgl data |
| 152 |
void Cell::GenLwmBgl(LwmBgl *bgl) |
| 153 |
{ |
| 154 |
int u, v, blocksz; |
| 155 |
LwmDataChunk *chunk = (LwmDataChunk *)bgl->NewDataChunk(x, y, bbox); |
| 156 |
|
| 157 |
for (u = 0; u < 32; u++) { |
| 158 |
for (v = 0; v < 32; v++) { |
| 159 |
area[u][v].mark = false; |
| 160 |
} |
| 161 |
} |
| 162 |
|
| 163 |
// First, generate AreaFill for water. |
| 164 |
for (blocksz = 16; blocksz >= 1; blocksz = blocksz / 2) { |
| 165 |
for (u = 0; u < 32; u += blocksz) { |
| 166 |
for (v = 0; v < 32; v += blocksz) { |
| 167 |
checkFill(chunk, u, v, blocksz); |
| 168 |
} |
| 169 |
} |
| 170 |
} |
| 171 |
|
| 172 |
// Next, fill all land area. |
| 173 |
for (u = 0; u < 32; u++) { |
| 174 |
for (v = 0; v < 32; v++) { |
| 175 |
if (area[u][v].getType() != T_LAND) continue; |
| 176 |
|
| 177 |
// near the shoreline?? |
| 178 |
const int K = AcOpts.landWidth; |
| 179 |
bool match = false; |
| 180 |
for (int uu = u - K; uu <= u + K; uu++) { |
| 181 |
for (int vv = v - K; vv <= v + K; vv++) { |
| 182 |
if (uu < 0 || uu >= 32 || |
| 183 |
vv < 0 || vv >= 32) continue; |
| 184 |
|
| 185 |
int t = area[uu][vv].getType(); |
| 186 |
if (t == T_MIX_WATER || t == T_MIX_LAND) { |
| 187 |
match = true; |
| 188 |
break; |
| 189 |
} |
| 190 |
} |
| 191 |
} |
| 192 |
|
| 193 |
if (match) { |
| 194 |
// fill this area... |
| 195 |
area[u][v].FillLand(chunk); |
| 196 |
} |
| 197 |
} |
| 198 |
} |
| 199 |
|
| 200 |
// Next, draw polygons. |
| 201 |
for (u = 0; u < 32; u++) { |
| 202 |
for (v = 0; v < 32; v++) { |
| 203 |
area[u][v].DrawLwmPolygon(chunk); |
| 204 |
} |
| 205 |
} |
| 206 |
} |
| 207 |
|
| 208 |
// |
| 209 |
// Generate VTP bgl data |
| 210 |
// |
| 211 |
void Cell::GenVtpBgl(VtpBgl *bgl) |
| 212 |
{ |
| 213 |
VtpDataChunk *chunk = (VtpDataChunk *)bgl->NewDataChunk(x, y, bbox); |
| 214 |
|
| 215 |
int level, i; |
| 216 |
GpcPolygon shorelines; |
| 217 |
|
| 218 |
// Extract shorelines from all polygons in the cell. |
| 219 |
for (level = 1; level <= 4; level++) { |
| 220 |
GpcPolygon *poly = cellData->get(level); |
| 221 |
|
| 222 |
if (poly->isEmpty() || |
| 223 |
poly->isFull(bbox)) continue; |
| 224 |
|
| 225 |
for (i = 0; i < poly->numVList(); i++) { |
| 226 |
ExtractShoreline(&shorelines, poly->getVList(i), level); |
| 227 |
} |
| 228 |
} |
| 229 |
|
| 230 |
int n = shorelines.numVList(); |
| 231 |
if (n == 0) { |
| 232 |
// No shorelines. |
| 233 |
return; |
| 234 |
} |
| 235 |
|
| 236 |
// Mask default VTP shoreline. |
| 237 |
for (int u = 0; u < 32; u++) { |
| 238 |
for (int v = 0; v < 32; v++) { |
| 239 |
chunk->DataArea(u, v, 0, 0); // numTexture=0, autocalc=0 |
| 240 |
} |
| 241 |
} |
| 242 |
|
| 243 |
|
| 244 |
// Start drawing polygons |
| 245 |
chunk->DataArea(x & 0x1f, y & 0x1f, 1, 1); // numTexture=1, autocalc=1 |
| 246 |
chunk->TextureStart(n); |
| 247 |
|
| 248 |
for (i = 0; i < n; i++) { |
| 249 |
// Interpolate vertices. |
| 250 |
// We must need at least one vertex in each LOD13 cell... |
| 251 |
GpcVertexList *vl = InterPolateVTPPoints(shorelines.getVList(i)); |
| 252 |
|
| 253 |
// Draw polygons |
| 254 |
int np = vl->numVertices(); |
| 255 |
chunk->PolyStart(np); |
| 256 |
for (int j = 0; j < np; j++) { |
| 257 |
double x = (vl->vx(j) - bbox.xmin) / (bbox.xmax - bbox.xmin) * |
| 258 |
(12240 - 4080) + 4080; |
| 259 |
double y = (bbox.ymax - vl->vy(j)) / (bbox.ymax - bbox.ymin) * |
| 260 |
(12240 - 4080) + 4080; |
| 261 |
|
| 262 |
chunk->WidePoint((int)x, (int)y, (j == 0) ? 1 : 0); |
| 263 |
if (j == 0) { |
| 264 |
chunk->WidePointWidth(AcOpts.textureWidth); |
| 265 |
} |
| 266 |
} |
| 267 |
|
| 268 |
delete vl; |
| 269 |
} |
| 270 |
} |
| 271 |
|
| 272 |
// |
| 273 |
// Extract shorelines |
| 274 |
// |
| 275 |
void Cell::ExtractShoreline(GpcPolygon *shorelines, GpcVertexList *vlist, int level) |
| 276 |
{ |
| 277 |
bool needInvert = false; |
| 278 |
|
| 279 |
// Fix draw starting point |
| 280 |
vlist->modifyStartPointFromEdge(bbox); |
| 281 |
|
| 282 |
// Check drawing order of the line. |
| 283 |
// Left side of the line will be land, right side will be water. |
| 284 |
// If not, we set needInvert flag to invert drawing order. |
| 285 |
if (vlist->isLeftHand()) { |
| 286 |
if (!isLand(level)) { |
| 287 |
needInvert = true; |
| 288 |
} |
| 289 |
} else { |
| 290 |
if (isLand(level)) { |
| 291 |
needInvert = true; |
| 292 |
} |
| 293 |
} |
| 294 |
|
| 295 |
// check drawing |
| 296 |
int drawStartPoint = -1; |
| 297 |
int n = vlist->numVertices(); |
| 298 |
int prev, flags = 0; |
| 299 |
bool allInside = true; |
| 300 |
|
| 301 |
for (int i = 0; i < n; i++) { |
| 302 |
prev = flags; |
| 303 |
flags = bbox.checkEdge(vlist->vx(i), vlist->vy(i)); |
| 304 |
if (flags) allInside = false; |
| 305 |
|
| 306 |
if (i == 0) continue; |
| 307 |
|
| 308 |
//fprintf(stderr, (flags) ? "_" : "*"); |
| 309 |
|
| 310 |
// check drawing start point |
| 311 |
if (drawStartPoint < 0) { |
| 312 |
// Check if we leaved from previous edge? |
| 313 |
if ((prev & ~flags) != 0 && |
| 314 |
(prev & flags) == 0) { |
| 315 |
// start drawing. |
| 316 |
drawStartPoint = i - 1; |
| 317 |
//fprintf(stderr, "S"); |
| 318 |
} |
| 319 |
} |
| 320 |
|
| 321 |
// check drawing end point |
| 322 |
if (drawStartPoint >= 0 && flags != 0) { |
| 323 |
// reached to edge. |
| 324 |
PutShoreLine(shorelines, vlist, |
| 325 |
drawStartPoint, i, needInvert); |
| 326 |
drawStartPoint = -1; |
| 327 |
//fprintf(stderr, "E"); |
| 328 |
} |
| 329 |
} |
| 330 |
//fprintf(stderr, "\n"); |
| 331 |
|
| 332 |
// No vertices are on any edge. Draw all lines... |
| 333 |
if (allInside) { |
| 334 |
PutShoreLine(shorelines, vlist, 0, n - 1, needInvert, true); |
| 335 |
} |
| 336 |
|
| 337 |
shorelines->BuildVertexListFromGpcPolygon(); |
| 338 |
} |
| 339 |
|
| 340 |
// |
| 341 |
// Put one shoreline |
| 342 |
// |
| 343 |
void Cell::PutShoreLine(GpcPolygon *shorelines, GpcVertexList *vlist, |
| 344 |
int st, int ed, bool needInvert, bool isLoop) |
| 345 |
{ |
| 346 |
ASSERT(ed > st); |
| 347 |
|
| 348 |
int n = ed - st + 1; |
| 349 |
if (isLoop) n++; |
| 350 |
|
| 351 |
GpcVertexList *vl = new GpcVertexList(n, NULL); |
| 352 |
|
| 353 |
gpc_vertex *s; |
| 354 |
gpc_vertex *d = vl->vertex(0); |
| 355 |
|
| 356 |
for (int i = 0; i < n ; i++) { |
| 357 |
if (isLoop && i == n - 1) { |
| 358 |
s = vlist->vertex(st); // loop |
| 359 |
} else { |
| 360 |
s = vlist->vertex(st + i); |
| 361 |
} |
| 362 |
*d++ = *s; |
| 363 |
} |
| 364 |
|
| 365 |
if (needInvert) { |
| 366 |
vl->invert(); |
| 367 |
} |
| 368 |
|
| 369 |
shorelines->AddVertexList(vl, false); |
| 370 |
} |
| 371 |
|
| 372 |
// LOD13 conversion |
| 373 |
inline int x2ax(double x, BoundingBox &bbox) |
| 374 |
{ |
| 375 |
int xx = (int)((x - bbox.xmin) / (bbox.xmax - bbox.xmin) * 32); |
| 376 |
if (xx == 32) xx = 31; |
| 377 |
return xx; |
| 378 |
} |
| 379 |
|
| 380 |
inline int y2ay(double y, BoundingBox &bbox) |
| 381 |
{ |
| 382 |
int yy = (int)((y - bbox.ymin) / (bbox.ymax - bbox.ymin) * 32); |
| 383 |
if (yy == 32) yy = 31; |
| 384 |
return yy; |
| 385 |
} |
| 386 |
|
| 387 |
// |
| 388 |
// Interpolate vertices |
| 389 |
// |
| 390 |
GpcVertexList *Cell::InterPolateVTPPoints(GpcVertexList *vl) |
| 391 |
{ |
| 392 |
int i, n; |
| 393 |
list<gpc_vertex>::iterator iter; |
| 394 |
|
| 395 |
// Create linked list of vertices. |
| 396 |
list<gpc_vertex> vlist; |
| 397 |
|
| 398 |
n = vl->numVertices(); |
| 399 |
for (i = 0; i < n; i++) { |
| 400 |
ASSERT(bbox.isInbox(vl->vx(i), vl->vy(i))); |
| 401 |
vlist.push_back(*vl->vertex(i)); |
| 402 |
} |
| 403 |
|
| 404 |
gpc_vertex v1, v2, vi; |
| 405 |
int ax1, ay1; |
| 406 |
int ax2, ay2; |
| 407 |
int ax3, ay3; |
| 408 |
int dx, dy; |
| 409 |
|
| 410 |
// check all sides... |
| 411 |
iter = vlist.begin(); |
| 412 |
for (;;) { |
| 413 |
v1 = *iter; |
| 414 |
iter++; |
| 415 |
if (iter == vlist.end()) break; |
| 416 |
v2 = *iter; |
| 417 |
|
| 418 |
ax1 = x2ax(v1.x, bbox); |
| 419 |
ay1 = y2ay(v1.y, bbox); |
| 420 |
|
| 421 |
ax2 = x2ax(v2.x, bbox); |
| 422 |
ay2 = y2ay(v2.y, bbox); |
| 423 |
|
| 424 |
// Check if interpolation needed? |
| 425 |
dx = abs(ax1 - ax2); |
| 426 |
dy = abs(ay1 - ay2); |
| 427 |
if ((dx == 0 && dy <= 1) || |
| 428 |
(dx <= 1 && dy == 0)) { |
| 429 |
// no interpolation |
| 430 |
continue; |
| 431 |
} |
| 432 |
|
| 433 |
// We need interpolation. |
| 434 |
// Find interpolation point from v1 to v2. |
| 435 |
Vector fv(v1.x, v1.y); |
| 436 |
Vector dv(v2.x - v1.x, v2.y - v1.y); |
| 437 |
Vector point; |
| 438 |
|
| 439 |
double k = 0.5, kd = 0.25; |
| 440 |
bool tryagain = false; |
| 441 |
for (;;) { |
| 442 |
point = fv + dv * k; |
| 443 |
ax3 = x2ax(point.x, bbox); |
| 444 |
ay3 = y2ay(point.y, bbox); |
| 445 |
|
| 446 |
if (ax3 == ax1 && ay3 == ay1) { |
| 447 |
k += kd; |
| 448 |
} |
| 449 |
else if (ax3 == ax2 && ay3 == ay2) { |
| 450 |
k -= kd; |
| 451 |
} |
| 452 |
else { |
| 453 |
// found! |
| 454 |
break; |
| 455 |
} |
| 456 |
|
| 457 |
kd = kd / 2.0; |
| 458 |
|
| 459 |
if (kd < 1.0e-10) { |
| 460 |
// oops! no interpolation point! |
| 461 |
// dirty hack... slightly move 2nd point... |
| 462 |
(*iter).y += 2.0e-6; |
| 463 |
tryagain = true; |
| 464 |
break; |
| 465 |
} |
| 466 |
} |
| 467 |
if (tryagain) { |
| 468 |
iter--; |
| 469 |
continue; |
| 470 |
} |
| 471 |
|
| 472 |
vi.x = point.x; |
| 473 |
vi.y = point.y; |
| 474 |
|
| 475 |
// Interpolate a point. |
| 476 |
vlist.insert(iter, vi); |
| 477 |
|
| 478 |
// Go back to a point before interpolated point. |
| 479 |
iter--; |
| 480 |
iter--; |
| 481 |
} |
| 482 |
|
| 483 |
// Create VertexList |
| 484 |
n = vlist.size(); |
| 485 |
GpcVertexList *nvl = new GpcVertexList(n, NULL); |
| 486 |
|
| 487 |
for (i = 0, iter = vlist.begin(); i < n; i++, iter++) { |
| 488 |
*(nvl->vertex(i)) = *iter; |
| 489 |
} |
| 490 |
ASSERT(iter == vlist.end()); |
| 491 |
|
| 492 |
return nvl; |
| 493 |
} |