terrain: 16-bit heightmap import/export
This commit is contained in:
+25
-16
@@ -197,13 +197,13 @@ HeightmapModifierWindow::HeightmapModifierWindow() : ModifierWindow("Heightmap")
|
||||
AddWidget(&scaleSlider);
|
||||
|
||||
loadButton.Create("Load Heightmap...");
|
||||
loadButton.SetTooltip("Load a heightmap texture, where the red channel corresponds to terrain height and the resolution to dimensions.\nThe heightmap will be placed in the world center.");
|
||||
loadButton.SetTooltip("Load a heightmap texture, where the red channel corresponds to terrain height and the resolution to dimensions.\nThe heightmap will be placed in the world center.\nIt is recommended to use a 16-bit PNG for heightmaps.");
|
||||
loadButton.OnClick([=](wi::gui::EventArgs args) {
|
||||
|
||||
wi::helper::FileDialogParams params;
|
||||
params.type = wi::helper::FileDialogParams::OPEN;
|
||||
params.description = "Texture";
|
||||
params.extensions = wi::resourcemanager::GetSupportedImageExtensions();
|
||||
params.description = "*.png";
|
||||
params.extensions = { "PNG" };
|
||||
wi::helper::FileDialog(params, [=](std::string fileName) {
|
||||
wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) {
|
||||
|
||||
@@ -212,21 +212,29 @@ HeightmapModifierWindow::HeightmapModifierWindow() : ModifierWindow("Heightmap")
|
||||
heightmap_modifier->width = 0;
|
||||
heightmap_modifier->height = 0;
|
||||
int bpp = 0;
|
||||
stbi_uc* rgba = stbi_load(fileName.c_str(), &heightmap_modifier->width, &heightmap_modifier->height, &bpp, 1);
|
||||
if (rgba != nullptr)
|
||||
if (stbi_is_16_bit(fileName.c_str()))
|
||||
{
|
||||
heightmap_modifier->data.resize(heightmap_modifier->width * heightmap_modifier->height);
|
||||
for (int i = 0; i < heightmap_modifier->width * heightmap_modifier->height; ++i)
|
||||
stbi_us* rgba = stbi_load_16(fileName.c_str(), &heightmap_modifier->width, &heightmap_modifier->height, &bpp, 1); if (rgba != nullptr)
|
||||
{
|
||||
heightmap_modifier->data[i] = rgba[i];
|
||||
heightmap_modifier->data.resize(heightmap_modifier->width * heightmap_modifier->height * sizeof(uint16_t));
|
||||
std::memcpy(heightmap_modifier->data.data(), rgba, heightmap_modifier->data.size());
|
||||
stbi_image_free(rgba);
|
||||
generation_callback(); // callback after heightmap load confirmation
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stbi_uc* rgba = stbi_load(fileName.c_str(), &heightmap_modifier->width, &heightmap_modifier->height, &bpp, 1);
|
||||
if (rgba != nullptr)
|
||||
{
|
||||
heightmap_modifier->data.resize(heightmap_modifier->width * heightmap_modifier->height * sizeof(uint8_t));
|
||||
std::memcpy(heightmap_modifier->data.data(), rgba, heightmap_modifier->data.size());
|
||||
generation_callback(); // callback after heightmap load confirmation
|
||||
}
|
||||
stbi_image_free(rgba);
|
||||
|
||||
generation_callback(); // callback after heightmap load confirmation
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
AddWidget(&loadButton);
|
||||
|
||||
SetSize(XMFLOAT2(200, 180));
|
||||
@@ -666,7 +674,7 @@ void TerrainWindow::Create(EditorComponent* _editor)
|
||||
AddWidget(&topLevelSlider);
|
||||
|
||||
saveHeightmapButton.Create("Save Heightmap...");
|
||||
saveHeightmapButton.SetTooltip("Save a heightmap texture from the currently generated terrain, where the red channel corresponds to terrain height and the resolution to dimensions.\nThe heightmap will be normalized into 8bit PNG format which can result in precision loss!");
|
||||
saveHeightmapButton.SetTooltip("Save a heightmap texture from the currently generated terrain, where the red channel corresponds to terrain height and the resolution to dimensions.\nThe image will be saved as a single channel 16-bit PNG.");
|
||||
saveHeightmapButton.SetSize(XMFLOAT2(wid, hei));
|
||||
saveHeightmapButton.SetPos(XMFLOAT2(x, y += step));
|
||||
AddWidget(&saveHeightmapButton);
|
||||
@@ -729,8 +737,9 @@ void TerrainWindow::Create(EditorComponent* _editor)
|
||||
wi::vector<uint8_t> data;
|
||||
int width = int(aabb.getHalfWidth().x * 2 + 1);
|
||||
int height = int(aabb.getHalfWidth().z * 2 + 1);
|
||||
data.resize(width * height);
|
||||
data.resize(width * height * sizeof(uint16_t));
|
||||
std::fill(data.begin(), data.end(), 0u);
|
||||
uint16_t* dest = (uint16_t*)data.data();
|
||||
|
||||
for (auto& chunk : terrain->chunks)
|
||||
{
|
||||
@@ -751,7 +760,7 @@ void TerrainWindow::Create(EditorComponent* _editor)
|
||||
p.x -= aabb._min.x;
|
||||
p.z -= aabb._min.z;
|
||||
int coord = int(p.x) + int(p.z) * width;
|
||||
data[coord] = uint8_t(wi::math::InverseLerp(aabb._min.y, aabb._max.y, p.y) * 255u);
|
||||
dest[coord] = uint16_t(wi::math::InverseLerp(aabb._min.y, aabb._max.y, p.y) * 65535u);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -767,7 +776,7 @@ void TerrainWindow::Create(EditorComponent* _editor)
|
||||
wi::graphics::TextureDesc desc;
|
||||
desc.width = uint32_t(width);
|
||||
desc.height = uint32_t(height);
|
||||
desc.format = wi::graphics::Format::R8_UNORM;
|
||||
desc.format = wi::graphics::Format::R16_UNORM;
|
||||
bool success = wi::helper::saveTextureToFile(data, desc, filename_replaced);
|
||||
assert(success);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Utility/stb_image_write.h"
|
||||
#include "Utility/basis_universal/encoder/basisu_comp.h"
|
||||
#include "Utility/basis_universal/encoder/basisu_gpu_texture.h"
|
||||
#include "Utility/basis_universal/encoder/lodepng.h"
|
||||
extern basist::etc1_global_selector_codebook g_basis_global_codebook;
|
||||
|
||||
#include <thread>
|
||||
@@ -219,6 +220,20 @@ namespace wi::helper
|
||||
using namespace wi::graphics;
|
||||
const uint32_t data_stride = GetFormatStride(desc.format);
|
||||
|
||||
std::string extension = wi::helper::toUpper(fileExtension);
|
||||
if (!extension.compare("PNG") && desc.format == Format::R16_UNORM)
|
||||
{
|
||||
// Specialized handling for 16-bit single channel PNG:
|
||||
uint16_t* dest = (uint16_t*)texturedata.data();
|
||||
for (uint32_t i = 0; i < desc.width * desc.height; ++i)
|
||||
{
|
||||
uint16_t value = dest[i];
|
||||
dest[i] = (value >> 8) | ((value & 0xFF) << 8); // little endian to big endian
|
||||
}
|
||||
lodepng::encode(filedata, texturedata, desc.width, desc.height, LCT_GREY, 16);
|
||||
return !filedata.empty();
|
||||
}
|
||||
|
||||
struct MipDesc
|
||||
{
|
||||
const uint8_t* address = nullptr;
|
||||
@@ -246,7 +261,6 @@ namespace wi::helper
|
||||
mip_depth = std::max(1u, mip_depth / 2);
|
||||
}
|
||||
|
||||
std::string extension = wi::helper::toUpper(fileExtension);
|
||||
bool basis = !extension.compare("BASIS");
|
||||
bool ktx2 = !extension.compare("KTX2");
|
||||
basisu::image basis_image;
|
||||
|
||||
@@ -390,11 +390,20 @@ namespace wi::terrain
|
||||
XMFLOAT2 p = world_pos;
|
||||
p.x *= frequency;
|
||||
p.y *= frequency;
|
||||
XMFLOAT2 pixel = XMFLOAT2(p.x + width * 0.5f, p.y + this->height * 0.5f);
|
||||
if (pixel.x >= 0 && pixel.x < width && pixel.y >= 0 && pixel.y < this->height)
|
||||
XMFLOAT2 pixel = XMFLOAT2(p.x + this->width * 0.5f, p.y + this->height * 0.5f);
|
||||
if (pixel.x >= 0 && pixel.x < this->width && pixel.y >= 0 && pixel.y < this->height)
|
||||
{
|
||||
const int idx = int(pixel.x) + int(pixel.y) * width;
|
||||
Blend(height, ((float)data[idx] / 255.0f) * scale);
|
||||
const int idx = int(pixel.x) + int(pixel.y) * this->width;
|
||||
float value = 0;
|
||||
if (data.size() == this->width * this->height * sizeof(uint8_t))
|
||||
{
|
||||
value = ((float)data[idx] / 255.0f);
|
||||
}
|
||||
else if (data.size() == this->width * this->height * sizeof(uint16_t))
|
||||
{
|
||||
value = ((float)((uint16_t*)data.data())[idx] / 65535.0f);
|
||||
}
|
||||
Blend(height, value * scale);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace wi::version
|
||||
// minor features, major updates, breaking compatibility changes
|
||||
const int minor = 71;
|
||||
// minor bug fixes, alterations, refactors, updates
|
||||
const int revision = 253;
|
||||
const int revision = 254;
|
||||
|
||||
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user