PixelBuf SteepnessMap::GetRegion( const MapPixelCoordInt &pos, const MapPixelDeltaInt &size) const { auto fixed_bounds_pb = GetRegion_BoundsHelper(*this, pos, size); if (fixed_bounds_pb.GetData()) return fixed_bounds_pb; double mpp; if (!MetersPerPixel(m_orig_map, pos + size/2, &mpp)) { // Return zero-initialized memory block. return PixelBuf(size.x, size.y); } unsigned int bezier_pixels = Bezier::N_POINTS - 1; double inv_bezier_meters = 1 / (bezier_pixels * mpp); MapPixelCoordInt req_pos = pos - MapPixelDeltaInt(1, 1); MapPixelDeltaInt req_size = size + MapPixelDeltaInt(2, 2); auto orig_data = m_orig_map->GetRegion(req_pos, req_size); PixelBuf result(size.x, size.y); unsigned int *src = orig_data.GetRawData(); unsigned int *dest = result.GetRawData(); for (int x=0; x < size.x; x++) { for (int y=0; y < size.y; y++) { MapPixelCoordInt pos(x+1, y+1); MapBezierGradient grad = Fast3x3CenterGradient(src, pos, req_size); grad *= inv_bezier_meters; double grad_steepness = atan(grad.Abs()); int color_index = static_cast<int>(grad_steepness / (M_PI / 2) * 18); DEST(x, y) = steepness_colors[color_index]; } } return result; }
void MapTerrainT::LoadHeightData(const wxString& FileName) { if (FileName.EndsWith(".ter")) { // Load terrain from Terragen file. FILE* FilePtr=fopen(FileName.c_str(), "rb"); if (FilePtr==NULL) throw BitmapT::LoadErrorT(); // Read header. char Header[20]; fread(Header, sizeof(char), 16, FilePtr); if (strncmp(Header, "TERRAGENTERRAIN ", 16)!=0) { fclose(FilePtr); throw BitmapT::LoadErrorT(); } // Header=="TERRAGENTERRAIN " // Reset the return values. unsigned int SizeX=0; unsigned int SizeY=0; // Other values from the file. VectorT MetersPerPixel(30.0, 30.0, 30.0); // The distance in meters between two neighboured pixels. // Read the chunks. while (true) { char ChunkMarker[10]; fread(ChunkMarker, sizeof(char), 4, FilePtr); if (feof(FilePtr)) break; if (strncmp(ChunkMarker, "EOF ", 4)==0) break; if (strncmp(ChunkMarker, "XPTS", 4)==0) { short int XPts; fread(&XPts, sizeof(XPts), 1, FilePtr); // Read two bytes. SizeX=XPts; // Let the XPTS chunk always override previous values (it appears only after SIZE anyway). fread(&XPts, sizeof(XPts), 1, FilePtr); // Overread padding bytes. } else if (strncmp(ChunkMarker, "YPTS", 4)==0) { short int YPts; fread(&YPts, sizeof(YPts), 1, FilePtr); // Read two bytes. SizeY=YPts; // Let the YPTS chunk always override previous values (it appears only after SIZE anyway). fread(&YPts, sizeof(YPts), 1, FilePtr); // Overread padding bytes. } else if (strncmp(ChunkMarker, "SIZE", 4)==0) { short int Size; fread(&Size, sizeof(Size), 1, FilePtr); // Read two bytes. SizeX=Size+1; // Note that the XPTS and YPTS chunks appear *after* the SIZE chunk. SizeY=Size+1; fread(&Size, sizeof(Size), 1, FilePtr); // Overread padding bytes. } else if (strncmp(ChunkMarker, "SCAL", 4)==0) { float Scale; fread(&Scale, sizeof(Scale), 1, FilePtr); MetersPerPixel.x=Scale; fread(&Scale, sizeof(Scale), 1, FilePtr); MetersPerPixel.y=Scale; fread(&Scale, sizeof(Scale), 1, FilePtr); MetersPerPixel.z=Scale; } else if (strncmp(ChunkMarker, "CRAD", 4)==0) { float PlanetRadius; fread(&PlanetRadius, sizeof(PlanetRadius), 1, FilePtr); // This value is ignored. } else if (strncmp(ChunkMarker, "CRVM", 4)==0) { unsigned long RenderMode; fread(&RenderMode, sizeof(RenderMode), 1, FilePtr); // This value is ignored. } else if (strncmp(ChunkMarker, "ALTW", 4)==0) { short int HeightScale; fread(&HeightScale, sizeof(HeightScale), 1, FilePtr); short int BaseHeight; fread(&BaseHeight, sizeof(BaseHeight), 1, FilePtr); if (SizeX!=SizeY) throw BitmapT::LoadErrorT(); m_Resolution=SizeX; m_HeightData.Clear(); m_HeightData.PushBackEmpty(m_Resolution*m_Resolution); for (unsigned long y=0; y<m_Resolution; y++) for (unsigned long x=0; x<m_Resolution; x++) { short int Elevation; fread(&Elevation, sizeof(Elevation), 1, FilePtr); m_HeightData[y*m_Resolution+x]=(unsigned short)(int(Elevation)+32768); } // Break here, because we're done *and* unspecified chunks like the THMB thumbnail-chunk might follow. // As I don't know how to deal with such chunks (where are the specs anyway?), just stop here. break; } else { // Unknown chunk? fclose(FilePtr); throw BitmapT::LoadErrorT(); } } fclose(FilePtr); } else if (FileName.EndsWith(".pgm")) { try { // Load terrain from Portable Gray-Map file (only the ASCII variant). TextParserT TP(FileName.c_str(), "", true, '#'); std::string MagicNumber=TP.GetNextToken(); bool IsAscii =(MagicNumber=="P2"); // If it's not P2 and not P5, something is wrong. if (!IsAscii && MagicNumber!="P5") throw TextParserT::ParseError(); unsigned int SizeX=TP.GetNextTokenAsInt(); unsigned int SizeY=TP.GetNextTokenAsInt(); double MaxVal =TP.GetNextTokenAsFloat(); if (SizeX!=SizeY) throw BitmapT::LoadErrorT(); m_Resolution=SizeX; m_HeightData.Clear(); m_HeightData.PushBackEmpty(m_Resolution*m_Resolution); if (IsAscii) { // It's the P2 ascii type. for (unsigned long y=0; y<m_Resolution; y++) for (unsigned long x=0; x<m_Resolution; x++) m_HeightData[(m_Resolution-y-1)*m_Resolution+x]=(unsigned short)((TP.GetNextTokenAsFloat()/MaxVal)*65535.0); } else { // It's the P5 binary type. FILE* FilePtr=fopen(FileName, "rb"); if (FilePtr==NULL) throw BitmapT::LoadErrorT(); // Assume that after the last text token (the maximum value) exactly *one* byte of white-space (e.g. '\r' or '\n') follows. fseek(FilePtr, TP.GetReadPosByte()+1, SEEK_SET); for (unsigned long y=0; y<m_Resolution; y++) for (unsigned long x=0; x<m_Resolution; x++) { unsigned char Value; fread(&Value, sizeof(Value), 1, FilePtr); // This probably slow as hell, but alas! Who cares? m_HeightData[(m_Resolution-y-1)*m_Resolution+x]=(unsigned short)((Value/MaxVal)*65535.0); } fclose(FilePtr); } } catch (const TextParserT::ParseError&) { throw BitmapT::LoadErrorT(); } } else { // Load terrain from image file. BitmapT HeightMap=BitmapT(FileName.c_str()); if (HeightMap.SizeX!=HeightMap.SizeY) throw BitmapT::LoadErrorT(); m_Resolution=HeightMap.SizeX; m_HeightData.Clear(); m_HeightData.PushBackEmpty(m_Resolution*m_Resolution); // Note that we pick the red channel of the RBG HeightMap.Data as the relevant channel. // Moreover, note that the y-axis of the HeightMap.Data points down in screen-space and thus towards us in world space. // Our world-space y-axis points opposite (away from us), though, and therefore we access the HeightMap.Data at (i, Size-j-1). for (unsigned long y=0; y<m_Resolution; y++) for (unsigned long x=0; x<m_Resolution; x++) m_HeightData[y*m_Resolution+x]=(unsigned short)((double(HeightMap.Data[(m_Resolution-y-1)*m_Resolution+x] & 0xFF)/255.0)*65535); } m_NeedsUpdate=true; }