static void MakeFractalTerrain (dFloat* const elevation, int sizeInPowerOfTwos, dFloat elevationScale, dFloat roughness, dFloat maxElevation, dFloat minElevation) { float* map[4096 + 1]; int size = (1 << sizeInPowerOfTwos) + 1; dAssert (size < int (sizeof (map) / sizeof map[0])); MakeMap (elevation, map, size); dFloat f = GetElevation (size, elevationScale, maxElevation, minElevation, roughness); map[0][0] = Guassian(f); map[0][size-1] = Guassian(f); map[size-1][0] = Guassian(f); map[size-1][size-1] = Guassian(f); for (int frequency = size - 1; frequency > 1; frequency = frequency / 2 ) { //dFloat f = pow (dFloat (frequency) * elevationScale, 1.0f + roughness); dFloat f = GetElevation (frequency, elevationScale, maxElevation, minElevation, roughness); for(int y0 = 0; y0 < (size - frequency); y0 += frequency) { int y1 = y0 + frequency / 2; int y2 = y0 + frequency; for(int x0 = 0; x0 < (size - frequency); x0 += frequency) { int x1 = x0 + frequency / 2; int x2 = x0 + frequency; map[y1][x1] = (map[y0][x0] + map[y0][x2] + map[y2][x0] + map[y2][x2]) * 0.25f + Guassian(f); map[y0][x1] = (map[y0][x0] + map[y0][x2]) * 0.5f + Guassian(f); map[y2][x1] = (map[y2][x0] + map[y2][x2]) * 0.5f + Guassian(f); map[y1][x0] = (map[y0][x0] + map[y2][x0]) * 0.5f + Guassian(f); map[y1][x2] = (map[y0][x2] + map[y2][x2]) * 0.5f + Guassian(f); // this trick eliminate the creases #ifdef RE_SAMPLE_CORNER map[y0][x0] = (map[y0][x1] + map[y1][x0]) * 0.5f + Guassian(f); map[y0][x2] = (map[y0][x1] + map[y1][x2]) * 0.5f + Guassian(f); map[y2][x0] = (map[y1][x0] + map[y2][x1]) * 0.5f + Guassian(f); map[y2][x2] = (map[y2][x1] + map[y1][x2]) * 0.5f + Guassian(f); // map[y0][x1] = (map[y0][x0] + map[y0][x2]) * 0.5f + Guassian(f); // map[y2][x1] = (map[y2][x0] + map[y2][x2]) * 0.5f + Guassian(f); // map[y1][x0] = (map[y0][x0] + map[y2][x0]) * 0.5f + Guassian(f); // map[y1][x2] = (map[y0][x2] + map[y2][x2]) * 0.5f + Guassian(f); #endif } } } }
void EstimateThermalBase(const GeoPoint location, const fixed altitude, const fixed average, const SpeedVector wind, GeoPoint &ground_location, fixed &ground_alt) { if (!positive(average)) return; // Time spent in last thermal fixed Tmax = altitude / average; // Shortcut if no terrain available if (terrain == NULL) { ground_location = FindLatitudeLongitude(location, wind.bearing, wind.norm * Tmax); ground_alt = fixed_zero; return; } // Time of the 10 calculation intervals fixed dt = Tmax / 10; RasterTerrain::Lease map(*terrain); GeoPoint loc; // Iterate over 10 time-based calculation intervals for (fixed t = fixed_zero; t <= Tmax; t += dt) { // Calculate position loc = FindLatitudeLongitude(location, wind.bearing, wind.norm * t); // Calculate altitude fixed hthermal = altitude - average * t; // Calculate altitude above ground fixed dh = hthermal - GetElevation(map, loc); // Below ground level if (negative(dh)) { // Calculate time when we passed the ground level t = t + dh / average; // Calculate position loc = FindLatitudeLongitude(location, wind.bearing, wind.norm * t); break; } } ground_location = loc; ground_alt = GetElevation(map, ground_location); }
/** * Count the number of unknown (invalid) heixels in this grid. */ int vtHeightFieldGrid3d::FindNumUnknown() { int count = 0; for (int i = 0; i < m_iSize.x; i++) for (int j = 0; j < m_iSize.y; j++) if (GetElevation(i, j) == INVALID_ELEVATION) count++; return count; }
/** * Use the height data in the grid and a colormap fill a bitmap with colors. * Any undefined heixels in the source will be fill with red (255,0,0). * * \param pBM The bitmap to be colored. * \param color_map A ColorMap which has already had GenerateColorTable() called. * \param nodata The color to use for NODATA areas, where there are no elevation values. * \param progress_callback If supplied, this function will be called back * with a value of 0 to 100 as the operation progresses. * * \return true if any invalid elevation values were encountered. */ bool vtHeightFieldGrid3d::ColorDibFromTable(vtBitmapBase *pBM, const ColorMap *color_map, const RGBAi &nodata, bool progress_callback(int)) const { VTLOG1(" ColorDibFromTable:"); const IPoint2 bitmap_size = pBM->GetSize(); int depth = pBM->GetDepth(); VTLOG(" dib size %d x %d, grid %d x %d.. ", bitmap_size.x, bitmap_size.y, m_iSize.x, m_iSize.y); const bool bExact = (bitmap_size == m_iSize); double ratiox = (double)(m_iSize.x - 1)/(bitmap_size.x - 1), ratioy = (double)(m_iSize.y - 1)/(bitmap_size.y - 1); bool has_invalid = false; const RGBi nodata_24bit(nodata.r, nodata.g, nodata.b); float elev; // now iterate over the texels for (int i = 0; i < bitmap_size.x; i++) { if (progress_callback != NULL && (i&40) == 0) progress_callback(i * 100 / bitmap_size.x); // find the corresponding location in the height grid const double x = i * ratiox; for (int j = 0; j < bitmap_size.y; j++) { const double y = j * ratioy; if (bExact) elev = GetElevation(i, j, true); // Always use true elevation else elev = GetInterpolatedElevation(x, y, true); // Always use true elevation if (elev == INVALID_ELEVATION) { if (depth == 32) pBM->SetPixel32(i, bitmap_size.y - 1 - j, nodata); else pBM->SetPixel24(i, bitmap_size.y - 1 - j, nodata_24bit); has_invalid = true; continue; } const RGBi &rgb = color_map->ColorFromTable(elev); if (depth == 32) pBM->SetPixel32(i, bitmap_size.y - 1 - j, rgb); else pBM->SetPixel24(i, bitmap_size.y - 1 - j, rgb); } } VTLOG("Done.\n"); return has_invalid; }
/** * Quickly produce a shading-like effect by scanning over the bitmap once, * using the east-west slope to produce lightening/darkening. * The bitmap must be the same size as the elevation grid, or a power of 2 smaller. */ void vtHeightFieldGrid3d::ShadeQuick(vtBitmapBase *pBM, float fLightFactor, bool bTrue, bool progress_callback(int)) { const IPoint2 bitmap_size = pBM->GetSize(); const int depth = pBM->GetDepth(); const int stepx = m_iSize.x / bitmap_size.x; const int stepy = m_iSize.y / bitmap_size.y; RGBi rgb; RGBAi rgba; for (int j = 0; j < bitmap_size.y; j++) { if (progress_callback != NULL && (j%40) == 0) progress_callback(j * 100 / bitmap_size.y); // find corresponding location in heightfield const int y = m_iSize.y-1 - (j * stepy); for (int i = 0; i < bitmap_size.x; i++) { if (depth == 32) pBM->GetPixel32(i, j, rgba); else pBM->GetPixel24(i, j, rgb); int x_offset = 0; if (i == bitmap_size.x-1) x_offset = -1; // index into elevation const int x = i * stepx; float value = GetElevation(x + x_offset, y, bTrue); if (value == INVALID_ELEVATION) { // Do not touch pixels in nodata areas continue; } float value2 = GetElevation(x+1 + x_offset, y, bTrue); if (value2 == INVALID_ELEVATION) value2 = value; short diff = (short) ((value2 - value) / m_fStep.x * fLightFactor); // clip to keep values under control if (diff > 128) diff = 128; else if (diff < -128) diff = -128; if (depth == 32) { rgba.r += diff; rgba.g += diff; rgba.b += diff; if (rgba.r < 0) rgba.r = 0; else if (rgba.r > 255) rgba.r = 255; if (rgba.g < 0) rgba.g = 0; else if (rgba.g > 255) rgba.g = 255; if (rgba.b < 0) rgba.b = 0; else if (rgba.b > 255) rgba.b = 255; pBM->SetPixel32(i, j, rgba); } else { rgb.r = rgb.r + diff; rgb.g = rgb.g + diff; rgb.b = rgb.b + diff; if (rgb.r < 0) rgb.r = 0; else if (rgb.r > 255) rgb.r = 255; if (rgb.g < 0) rgb.g = 0; else if (rgb.g > 255) rgb.g = 255; if (rgb.b < 0) rgb.b = 0; else if (rgb.b > 255) rgb.b = 255; pBM->SetPixel24(i, j, rgb); } } } }
/** * Get the interpolated height of the grid at a specific grid coordinate, * where the coordinates can be non-integer; the result is interpolated * between the source heixels when possible (i.e. not at the edge) * * \param findex_x Floating point index, from 0 to width in heixels. * \param findex_y Floating point index, from 0 to height in heixels. * \param bTrue Use the true elevation, ignoring any scaling/exaggeration. */ float vtHeightFieldGrid3d::GetInterpolatedElevation(double findex_x, double findex_y, bool bTrue) const { // Require the point to be inside the grid if (findex_x < 0 || findex_x > m_iSize.x-1) return INVALID_ELEVATION; if (findex_y < 0 || findex_y > m_iSize.y-1) return INVALID_ELEVATION; int index_x = (int) findex_x; int index_y = (int) findex_y; float diff_x = (float) (findex_x - index_x); float diff_y = (float) (findex_y - index_y); if (index_x == m_iSize.x-1) { // On right edge index_x --; diff_x = 1.0f; } if (index_y == m_iSize.y-1) { // On top edge index_y --; diff_y = 1.0f; } const float fDataBL = GetElevation(index_x, index_y, bTrue); const float fDataBR = GetElevation(index_x+1, index_y, bTrue); const float fDataTL = GetElevation(index_x, index_y+1, bTrue); const float fDataTR = GetElevation(index_x+1, index_y+1, bTrue); int valid = 0; if (fDataBL != INVALID_ELEVATION) valid++; if (fDataBR != INVALID_ELEVATION) valid++; if (fDataTL != INVALID_ELEVATION) valid++; if (fDataTR != INVALID_ELEVATION) valid++; float fData; if (valid == 4) // all valid { // do bilinear filtering fData = (float) (fDataBL + (fDataBR-fDataBL)*diff_x + (fDataTL-fDataBL)*diff_y + (fDataTR-fDataTL-fDataBR+fDataBL)*diff_x*diff_y); } else if (valid > 0) { // Look for closest valid nearest neighbor float dist[4]; float value[4]; value[0] = fDataBL; value[1] = fDataBR; value[2] = fDataTL; value[3] = fDataTR; if (fDataBL != INVALID_ELEVATION) dist[0] = fabs(diff_x*diff_x) + fabs(diff_y*diff_y); else dist[0] = 3; // Not valid, use a value > 2 if (fDataBR != INVALID_ELEVATION) dist[1] = fabs((1-diff_x)*(1-diff_x)) + fabs(diff_y*diff_y); else dist[1] = 3; if (fDataTL != INVALID_ELEVATION) dist[2] = fabs(diff_x*diff_x) + fabs((1-diff_y)*(1-diff_y)); else dist[2] = 3; if (fDataTR != INVALID_ELEVATION) dist[3] = fabs((1-diff_x)*(1-diff_x)) + fabs((1-diff_y)*(1-diff_y)); else dist[3] = 3; float closest = 4; int closest_index; for (int i = 0; i < 4; i++) { if (dist[i] < closest) { closest = dist[i]; closest_index = i; } } fData = value[closest_index]; } else fData = INVALID_ELEVATION; return fData; }
void EstimateThermalBase(const RasterTerrain *terrain, const GeoPoint location, const double altitude, const double average, const SpeedVector wind, GeoPoint &ground_location, double &ground_alt) { if (average <= 0 || altitude <= 0) { ground_location = location; ground_alt = 0; return; } // Max time the thermal could have risen for if ground // elevation is zero const auto Tmax = altitude / average; // Shortcut if no terrain available if (terrain == NULL) { ground_location = FindLatitudeLongitude(location, wind.bearing, wind.norm * Tmax); ground_alt = 0; return; } RasterTerrain::Lease map(*terrain); // Height step of the 10 calculation intervals const auto dh = altitude / 10; // Iterate over 10 altitude-based calculation intervals // We do this because the terrain elevation may shift // as we trace the thermal back to its source GeoPoint loc = location; for (auto h = altitude; h >= 0; h -= dh) { // Time to descend to this height auto t = (altitude - h) / average; // Calculate position loc = FindLatitudeLongitude(location, wind.bearing, wind.norm * t); // Calculate altitude above ground auto dh = h - GetElevation(map, loc); // At or below ground level, use linear interpolation // to estimate intersection if (dh <= 0) { // Calculate time when we passed the ground level t += dh / average; if (t <= 0) /* can happen when the terrain at this location is higher than the aircraft's current altitude; bail out */ break; // Calculate position loc = FindLatitudeLongitude(location, wind.bearing, wind.norm * t); break; } } ground_location = loc; ground_alt = GetElevation(map, ground_location); }
/** * Quickly produce a shading-like effect by scanning over the bitmap once, * using the east-west slope to produce lightening/darkening. * The bitmap must be the same size as the elevation grid, or a power of 2 smaller. */ void vtHeightFieldGrid3d::ShadeQuick(vtBitmapBase *pBM, float fLightFactor, bool bTrue, bool progress_callback(int)) { int w = pBM->GetWidth(); int h = pBM->GetHeight(); int depth = pBM->GetDepth(); int stepx = m_iColumns / w; int stepy = m_iRows / h; int i, j; // indices into bitmap int x, y; // indices into elevation RGBi rgb; RGBAi rgba; for (j = 0; j < h; j++) { if (progress_callback != NULL) { if ((j&7) == 0) progress_callback(j * 100 / h); } // find corresponding location in heightfield y = m_iRows-1 - (j * stepy); for (i = 0; i < w; i++) { if (depth == 32) pBM->GetPixel32(i, j, rgba); else pBM->GetPixel24(i, j, rgb); int x_offset = 0; if (i == w-1) x_offset = -1; x = i * stepx; float value = GetElevation(x + x_offset, y, bTrue); if (value == INVALID_ELEVATION) { // Do not touch pixels in nodata areas continue; } float value2 = GetElevation(x+1 + x_offset, y, bTrue); if (value2 == INVALID_ELEVATION) value2 = value; short diff = (short) ((value2 - value) / m_fXStep * fLightFactor); // clip to keep values under control if (diff > 128) diff = 128; else if (diff < -128) diff = -128; if (depth == 32) { rgba.r += diff; rgba.g += diff; rgba.b += diff; if (rgba.r < 0) rgba.r = 0; else if (rgba.r > 255) rgba.r = 255; if (rgba.g < 0) rgba.g = 0; else if (rgba.g > 255) rgba.g = 255; if (rgba.b < 0) rgba.b = 0; else if (rgba.b > 255) rgba.b = 255; pBM->SetPixel32(i, j, rgba); } else { rgb.r = rgb.r + diff; rgb.g = rgb.g + diff; rgb.b = rgb.b + diff; if (rgb.r < 0) rgb.r = 0; else if (rgb.r > 255) rgb.r = 255; if (rgb.g < 0) rgb.g = 0; else if (rgb.g > 255) rgb.g = 255; if (rgb.b < 0) rgb.b = 0; else if (rgb.b > 255) rgb.b = 255; pBM->SetPixel24(i, j, rgb); } } } }
/** * Use the height data in the grid and a colormap fill a bitmap with colors. * Any undefined heixels in the source will be fill with red (255,0,0). * * \param pBM The bitmap to be colored. * \param table The table of colors. * \param fMin, fMax The range of valid elevation values expect in the input. * \param nodata The color to use for NODATA areas, where there are no elevation values. * \param progress_callback If supplied, this function will be called back * with a value of 0 to 100 as the operation progresses. * * \return true if any invalid elevation values were encountered. */ bool vtHeightFieldGrid3d::ColorDibFromTable(vtBitmapBase *pBM, std::vector<RGBi> &table, float fMin, float fMax, const RGBAi &nodata, bool progress_callback(int)) { VTLOG1(" ColorDibFromTable:"); int w = pBM->GetWidth(); int h = pBM->GetHeight(); int depth = pBM->GetDepth(); int gw, gh; GetDimensions(gw, gh); VTLOG(" dib size %d x %d, grid %d x %d.. ", w, h, gw, gh); bool bExact = (w == gw && h == gh); double ratiox = (double)(gw-1)/(w-1), ratioy = (double)(gh-1)/(h-1); float fRange = fMax - fMin; uint iGranularity = table.size()-1; bool has_invalid = false; RGBi nodata_24bit(nodata.r, nodata.g, nodata.b); double x, y; float elev; // now iterate over the texels for (int i = 0; i < w; i++) { if (progress_callback != NULL) { if ((i&7) == 0) progress_callback(i * 100 / w); } x = i * ratiox; // find corresponding location in height grid for (int j = 0; j < h; j++) { y = j * ratioy; if (bExact) elev = GetElevation(i, j); else elev = GetInterpolatedElevation(x, y); if (elev == INVALID_ELEVATION) { if (depth == 32) pBM->SetPixel32(i, h-1-j, nodata); else pBM->SetPixel24(i, h-1-j, nodata_24bit); has_invalid = true; continue; } uint table_entry = (uint) ((elev - fMin) / fRange * iGranularity); if (table_entry > iGranularity-1) table_entry = iGranularity-1; if (depth == 32) pBM->SetPixel32(i, h-1-j, table[table_entry]); else pBM->SetPixel24(i, h-1-j, table[table_entry]); } } VTLOG("Done.\n"); return has_invalid; }