diff --git a/Editor/TerrainWindow.cpp b/Editor/TerrainWindow.cpp index 5d1f0ac8e..95a6499fe 100644 --- a/Editor/TerrainWindow.cpp +++ b/Editor/TerrainWindow.cpp @@ -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 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); diff --git a/WickedEngine/wiHelper.cpp b/WickedEngine/wiHelper.cpp index 972d2b226..8c5a8cd7a 100644 --- a/WickedEngine/wiHelper.cpp +++ b/WickedEngine/wiHelper.cpp @@ -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 @@ -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; diff --git a/WickedEngine/wiTerrain.h b/WickedEngine/wiTerrain.h index 77bfd4749..de7791437 100644 --- a/WickedEngine/wiTerrain.h +++ b/WickedEngine/wiTerrain.h @@ -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); } } }; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 629d72bb4..e9b11cbb1 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -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);