Add options to generate a height map of the world.

--heightmap: generate the height map, in colors
--heightmap-grey: use shades of grey instead of colors
--sealevel <n>: define the sea level (below sea level is drawn in blue)
--heightmap-scale <f>: scale the heights by f (for the purpose of
  color selection)

When generating a heightmap, a special colors file is needed,
that defines just the blocks that should be considered part
of the ground. That means that normally, any plants, special
nodes and water should not be included.
This commit is contained in:
Rogier 2015-01-24 19:40:42 +01:00
parent 2d3eaff736
commit 3812e31fc9
6 changed files with 1301 additions and 5 deletions

View File

@ -296,6 +296,7 @@ if(WIN32)
install(FILES ${META_FILES} DESTINATION ".")
install(FILES "colors.txt" DESTINATION ".")
install(FILES "colors-heightmap.txt" DESTINATION ".")
install(FILES "colors-average-alpha.txt" DESTINATION ".")
install(FILES "colors-cumulative-alpha.txt" DESTINATION ".")
install(PROGRAMS "${PROJECT_BINARY_DIR}/minetestmapper.exe" DESTINATION ".")
@ -318,6 +319,7 @@ elseif(CREATE_FLAT_PACKAGE)
install(FILES ${META_FILES} DESTINATION ".")
install(FILES "colors.txt" DESTINATION ".")
install(FILES "colors-heightmap.txt" DESTINATION ".")
install(FILES "colors-average-alpha.txt" DESTINATION ".")
install(FILES "colors-cumulative-alpha.txt" DESTINATION ".")
install(PROGRAMS "${PROJECT_BINARY_DIR}/minetestmapper" DESTINATION ".")
@ -352,6 +354,7 @@ else(WIN32)
install(FILES ${META_FILES} DESTINATION "share/doc/${PROJECT_NAME}" COMPONENT mapper)
install(FILES colors.txt DESTINATION "share/games/${PROJECT_NAME}" COMPONENT mapper)
install(FILES colors-heightmap.txt DESTINATION "share/games/${PROJECT_NAME}" COMPONENT mapper)
install(FILES colors-average-alpha.txt DESTINATION "share/games/${PROJECT_NAME}" COMPONENT mapper)
install(FILES colors-cumulative-alpha.txt DESTINATION "share/games/${PROJECT_NAME}" COMPONENT mapper)
install(TARGETS minetestmapper RUNTIME DESTINATION bin COMPONENT mapper)

View File

@ -142,6 +142,37 @@ colors <file>:
default:water_source -
default:water_flowing -
heightmap[-grey]:
Draw a height-map instead of a regular map.
A height map needs a custom colors file that contains only the nodes that
define the height. As a general directive, plants and other special nodes
should not be included in the file. Normally, water nodes should not be
included either.
The colors used are predefined and not yet configurable.
The colors support a maximum depth below sea level of 60, although below a
depth of 30 they become progressively darker.
Above sea level, they support a maximum height of 250.
Starting from height level 70, greyish colors are used (think: 'rocky')
Starting from height level 111, shades of white are used (think: 'snowy').
For '--heightmap-grey', a 255-level greyscale map is generated, with colors
ranging from black for level 0 and below, to white for 255 and above.
heightmap-scale
Scale the heights of the map before computing the colors.
Note that the water level is not rendered correctly for scale factors
smaller than 1, nor for small non-integer scale factors.
sealevel:
Set the sea level for the height map. Nodes at or below sea level are
drawn in blue, nodes above sea level are drawn in other colors.
The sea level *does* *not* reflect the presence of actual water.
bgcolor:
Background color of image, `--bgcolor #ffffff`

View File

@ -121,11 +121,21 @@ static inline int readBlockContent(const unsigned char *mapData, int version, in
static const ColorEntry nodeColorNotDrawnObject;
const ColorEntry *TileGenerator::NodeColorNotDrawn = &nodeColorNotDrawnObject;
struct HeightMapColor
{
int height[2];
Color color[2];
};
TileGenerator::TileGenerator():
verboseCoordinates(0),
verboseReadColors(0),
verboseStatistics(false),
progressIndicator(false),
m_heightMap(false),
m_heightMapGrey(false),
m_heightMapYScale(1),
m_seaLevel(0),
m_bgColor(255, 255, 255),
m_blockDefaultColor(0, 0, 0, 0),
m_scaleColor(0, 0, 0),
@ -176,6 +186,45 @@ TileGenerator::~TileGenerator()
{
}
void TileGenerator::setHeightMap(bool enable, bool grey)
{
m_heightMap = enable;
m_heightMapGrey = grey;
m_heightMapColors.clear();
if (m_heightMapGrey) {
m_heightMapColors.push_back(HeightMapColor(INT_MIN, Color(0,0,0), -1, Color(0,0,0)));
m_heightMapColors.push_back(HeightMapColor(0, Color(0,0,0), 255, Color(255,255,255)));
m_heightMapColors.push_back(HeightMapColor(256, Color(255,255,255), INT_MAX, Color(255,255,255)));
}
else {
m_heightMapColors.push_back(HeightMapColor(INT_MIN, Color(4,4,4), -60, Color(4,4,4)));
m_heightMapColors.push_back(HeightMapColor(-60, Color(4,4,4), -45, Color(16,16,16)));
m_heightMapColors.push_back(HeightMapColor(-45, Color(16,16,16), -30, Color(16,32,64)));
m_heightMapColors.push_back(HeightMapColor(-30, Color(16,32,64), 0, Color(32,64,255)));
m_heightMapColors.push_back(HeightMapColor(1, Color(32,128,32), 10, Color(64,192,64))); // Green
m_heightMapColors.push_back(HeightMapColor(10, Color(64,192,64), 20, Color(192,192,64))); // Green -> Yellow
m_heightMapColors.push_back(HeightMapColor(20, Color(192,192,64), 40, Color(128,128,64))); // Yellow
m_heightMapColors.push_back(HeightMapColor(40, Color(128,128,64), 50, Color(128,64,64))); // Yellow -> Red
m_heightMapColors.push_back(HeightMapColor(50, Color(128,64,64), 70, Color(64,32,32))); // Red
m_heightMapColors.push_back(HeightMapColor(70, Color(64,32,32), 80, Color(48,48,48))); // Red -> Grey
m_heightMapColors.push_back(HeightMapColor(80, Color(48,48,48), 140, Color(96,96,96))); // Grey
// Above 100, white suggests snowy :-)
m_heightMapColors.push_back(HeightMapColor(141, Color(160,160,160), 250, Color(250,250,250))); // Grey -> White
m_heightMapColors.push_back(HeightMapColor(250, Color(250,250,250), INT_MAX, Color(250,250,250)));
}
}
void TileGenerator::setHeightMapYScale(float scale)
{
m_heightMapYScale = scale;
}
void TileGenerator::setSeaLevel(int level)
{
m_seaLevel = level;
}
void TileGenerator::setBgColor(const Color &bgColor)
{
m_bgColor = bgColor;
@ -1294,24 +1343,51 @@ inline void TileGenerator::renderMapBlock(const ustring &mapBlock, const BlockPo
for (int y = maxY; y >= minY; --y) {
int position = x + (y << 4) + (z << 8);
int content = readBlockContent(mapData, version, position);
#define nodeColor (*m_nodeIDColor[content])
//const ColorEntry &nodeColor = *m_nodeIDColor[content];
if (m_nodeIDColor[content] == NodeColorNotDrawn) {
continue;
}
if (m_nodeIDColor[content]) {
int height = pos.y * 16 + y;
if (m_heightMap) {
if (m_nodeIDColor[content] && nodeColor.a != 0) {
int adjustedHeight = int((height - m_seaLevel) * m_heightMapYScale + 0.5);
rowIsEmpty = false;
#define nodeColor (*m_nodeIDColor[content])
//const ColorEntry &nodeColor = *m_nodeIDColor[content];
pixel.mixUnder(PixelAttribute(nodeColor, pos.y * 16 + y));
float r = 0;
float g = 0;
float b = 0;
int n = 0;
for (std::list<HeightMapColor>::iterator i = m_heightMapColors.begin(); i != m_heightMapColors.end(); i++) {
HeightMapColor &colorSpec = *i;
if (adjustedHeight >= colorSpec.height[0] && adjustedHeight <= colorSpec.height[1]) {
float weight = (float) (colorSpec.height[1] - adjustedHeight + 1) / (colorSpec.height[1] - colorSpec.height[0] + 1);
for (int j = 0; j < 2; j++) {
r += colorSpec.color[j].r * weight;
g += colorSpec.color[j].g * weight;
b += colorSpec.color[j].b * weight;
weight = 1 - weight;
}
n++;
}
}
pixel = PixelAttribute(Color(int(r / n + 0.5), int(g / n + 0.5), int(b / n + 0.5)), height);
m_readedPixels[z] |= (1 << x);
break;
}
}
else if (m_nodeIDColor[content]) {
rowIsEmpty = false;
pixel.mixUnder(PixelAttribute(nodeColor, height));
if ((m_drawAlpha && nodeColor.a == 0xff) || (!m_drawAlpha && nodeColor.a != 0)) {
m_readedPixels[z] |= (1 << x);
break;
}
#undef nodeColor
} else {
NodeID2NameMap::iterator blockName = m_nameMap.find(content);
if (blockName != m_nameMap.end())
m_unknownNodes.insert(blockName->second);
}
#undef nodeColor
}
#undef pixel
}

View File

@ -20,6 +20,7 @@
#include <map>
#endif
#include <set>
#include <list>
#include <stdint.h>
#include <string>
#include <string>
@ -57,6 +58,12 @@ private:
typedef std::map<int, std::string> NodeID2NameMap;
#endif
public:
struct HeightMapColor
{
HeightMapColor(int h0, Color c0, int h1, Color c1) : height{h0, h1}, color{c0, c1} {}
int height[2];
Color color[2];
};
struct DrawObject {
void setCenter(const NodeCoord &c) { haveCenter = true; center = c; }
void setCorner1(const NodeCoord &c) { haveCenter = false; corner1 = c; }
@ -94,6 +101,9 @@ public:
TileGenerator();
~TileGenerator();
void setHeightMap(bool enable, bool grey);
void setHeightMapYScale(float scale);
void setSeaLevel(int level);
void setBgColor(const Color &bgColor);
void setBlockDefaultColor(const Color &olor);
void setScaleColor(const Color &scaleColor);
@ -177,6 +187,11 @@ public:
bool progressIndicator;
private:
bool m_heightMap;
bool m_heightMapGrey;
float m_heightMapYScale;
int m_seaLevel;
std::list<HeightMapColor> m_heightMapColors;
Color m_bgColor;
Color m_blockDefaultColor;
Color m_scaleColor;

1130
colors-heightmap.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,11 @@ using namespace std;
#define OPT_DRAWAIR 0x85
#define OPT_VERBOSE_SEARCH_COLORS 0x86
#define OPT_CHUNKSIZE 0x87
#define OPT_HEIGHTMAP 0x88
#define OPT_HEIGHTMAPGREY 0x89
#define OPT_HEIGHTMAPYSCALE 0x8a
#define OPT_HEIGHT_LEVEL0 0x8b
// Will be replaced with the actual name and location of the executable (if found)
string executableName = "minetestmapper";
@ -66,6 +71,9 @@ void usage()
" -i/--input <world_path>\n"
" -o/--output <output_image.png>\n"
" --colors <file>\n"
" --heightmap[-grey]\n"
" --heightmap-scale <scale>\n"
" --height-level-0 <level>\n"
" --bgcolor <color>\n"
" --blockcolor <color>\n"
" --scalecolor <color>\n"
@ -522,6 +530,11 @@ int main(int argc, char *argv[])
{"input", required_argument, 0, 'i'},
{"output", required_argument, 0, 'o'},
{"colors", required_argument, 0, 'C'},
{"heightmap", no_argument, 0, OPT_HEIGHTMAP},
{"heightmap-grey", no_argument, 0, OPT_HEIGHTMAPGREY},
{"heightmap-gray", no_argument, 0, OPT_HEIGHTMAPGREY},
{"heightmap-scale", required_argument, 0, OPT_HEIGHTMAPYSCALE},
{"height-level-0", required_argument, 0, OPT_HEIGHT_LEVEL0},
{"bgcolor", required_argument, 0, 'b'},
{"blockcolor", required_argument, 0, OPT_BLOCKCOLOR},
{"scalecolor", required_argument, 0, 's'},
@ -606,6 +619,34 @@ int main(int argc, char *argv[])
case 'b':
generator.setBgColor(Color(optarg, 0));
break;
case OPT_HEIGHTMAP:
generator.setHeightMap(true, false);
break;
case OPT_HEIGHTMAPGREY:
generator.setHeightMap(true, true);
break;
case OPT_HEIGHTMAPYSCALE:
if (isdigit(optarg[0]) || ((optarg[0]=='-' || optarg[0]=='+') && isdigit(optarg[1]))) {
float scale = atof(optarg);
generator.setHeightMapYScale(scale);
}
else {
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': '" << optarg << "'" << std::endl;
usage();
exit(1);
}
break;
case OPT_HEIGHT_LEVEL0:
if (isdigit(optarg[0]) || ((optarg[0]=='-' || optarg[0]=='+') && isdigit(optarg[1]))) {
int level = atoi(optarg);
generator.setSeaLevel(level);
}
else {
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': '" << optarg << "'" << std::endl;
usage();
exit(1);
}
break;
case OPT_BLOCKCOLOR:
generator.setBlockDefaultColor(Color(optarg, 0));
break;