So recently I had been trying to figure out how to properly load in 16-bit heightmaps. Using a library called lodepng (and cadaver’s help), I was able to solve this issue.
Problem:
Goal:
Code:
Load in your 16bit png
std::vector<unsigned char> pngData;
std::vector<unsigned char> image;
unsigned width = 0, height = 0;
lodepng::State state;
unsigned error = lodepng::load_file(pngData, rawImageFilePath.CString());
if (!error) error = lodepng::decode(image, width, height, pngData, LCT_GREY, 16);
if (error) {
URHO3D_LOGERROR(String("Unabled to load raw PNG file @ ") + rawImageFilePath);
return nullptr;
}
Convert 16bit to 8bit by packing data into the R and G channels as told in the Terrain doc
// Storage for our 16bit pixel.
union uimage_data
{
uint16_t s;
uint8_t b[2];
};
Image* img = new Image(context_);
unsigned components = 2; // R & G channels.
unsigned dataLen = width * height * components;
unsigned char* pixelData = new unsigned char[dataLen];
img->SetSize(width, height, components);
unsigned imgDataIndex = 0;
unsigned index = 0;
unsigned imgSizeHalf = static_cast<unsigned>(image.size() / 2);
uimage_data* sData = new uimage_data[imgSizeHalf];
// Copy over the pixel data into our storage.
for (index = 0; index < imgSizeHalf; index += 2, imgDataIndex += 1) {
// Store in big-endian which is what lodepng uses.
sData[imgDataIndex].b[0] = image[index + 1];
sData[imgDataIndex].b[1] = image[index];
}
// Update our pixel data for Urho3D.
imgDataIndex = 0;
for (index = 0; index < dataLen; index += components, imgDataIndex += 2) {
pixelData[index] = sData[imgDataIndex].b[0];
pixelData[index + 1] = sData[imgDataIndex].b[1];
}
There are probably optimizations that can be done. I need to go over the code once more, but so far this works. I hope this helps anyone else that may have the same issue.