From 8582ea3dc324e2a1a438f346e3e2a87d38eca2bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Mon, 28 Apr 2025 18:20:15 +0200 Subject: [PATCH] Terrain determinism fixes (#1096) --- Editor/TerrainWindow.cpp | 157 +++++++++++++---------- Editor/TerrainWindow.h | 6 +- WickedEngine/wiGraphicsDevice_DX12.cpp | 7 +- WickedEngine/wiGraphicsDevice_Vulkan.cpp | 6 +- WickedEngine/wiNoise.h | 127 ++++++++++++++---- WickedEngine/wiRandom.h | 8 +- WickedEngine/wiTerrain.cpp | 36 ++---- WickedEngine/wiTerrain.h | 12 +- WickedEngine/wiVersion.cpp | 2 +- 9 files changed, 222 insertions(+), 139 deletions(-) diff --git a/Editor/TerrainWindow.cpp b/Editor/TerrainWindow.cpp index a98237479..eb39c5985 100644 --- a/Editor/TerrainWindow.cpp +++ b/Editor/TerrainWindow.cpp @@ -19,7 +19,7 @@ ModifierWindow::ModifierWindow(const std::string& name) blendCombo.OnSelect([=](wi::gui::EventArgs args) { modifier->blend = (wi::terrain::Modifier::BlendMode)args.userdata; generation_callback(); - }); + }); AddWidget(&blendCombo); weightSlider.Create(0, 1, 0.5f, 10000, "Weight: "); @@ -28,31 +28,31 @@ ModifierWindow::ModifierWindow(const std::string& name) weightSlider.OnSlide([=](wi::gui::EventArgs args) { modifier->weight = args.fValue; generation_callback(); - }); + }); AddWidget(&weightSlider); - frequencySlider.Create(0.0001f, 0.01f, 0.0008f, 10000, "Frequency: "); - frequencySlider.SetSize(XMFLOAT2(100, 20)); - frequencySlider.SetTooltip("Frequency for the tiling"); - frequencySlider.OnSlide([=](wi::gui::EventArgs args) { - modifier->frequency = args.fValue; + scaleSlider.Create(0.1f, 10000, 1000, 10000, "Scale: "); + scaleSlider.SetSize(XMFLOAT2(100, 20)); + scaleSlider.SetTooltip("Horizontal world scale of the modifier"); + scaleSlider.OnSlide([=](wi::gui::EventArgs args) { + modifier->SetScale(args.fValue); generation_callback(); - }); - AddWidget(&frequencySlider); + }); + AddWidget(&scaleSlider); } void ModifierWindow::Bind(wi::terrain::Modifier* ptr) { modifier = ptr; modifier->blend = (wi::terrain::Modifier::BlendMode)blendCombo.GetItemUserData(blendCombo.GetSelected()); modifier->weight = weightSlider.GetValue(); - modifier->frequency = frequencySlider.GetValue(); + modifier->SetScale(scaleSlider.GetValue()); } void ModifierWindow::From(wi::terrain::Modifier* ptr) { modifier = ptr; blendCombo.SetSelectedByUserdataWithoutCallback((uint64_t)ptr->blend); weightSlider.SetValue(ptr->weight); - frequencySlider.SetValue(ptr->frequency); + scaleSlider.SetValue(ptr->GetScale()); } PerlinModifierWindow::PerlinModifierWindow() : ModifierWindow("Perlin Noise") @@ -87,7 +87,7 @@ void PerlinModifierWindow::ResizeLayout() add(blendCombo); add(weightSlider); - add(frequencySlider); + add(scaleSlider); add(octavesSlider); } @@ -161,7 +161,7 @@ void VoronoiModifierWindow::ResizeLayout() add(blendCombo); add(weightSlider); - add(frequencySlider); + add(scaleSlider); add(fadeSlider); add(shapeSlider); @@ -188,15 +188,15 @@ void VoronoiModifierWindow::From(wi::terrain::VoronoiModifier* ptr) HeightmapModifierWindow::HeightmapModifierWindow() : ModifierWindow("Heightmap") { weightSlider.SetValue(1); - frequencySlider.SetValue(1); + amountSlider.SetValue(1000); - scaleSlider.Create(0, 1, 0.1f, 1000, "Scale: "); - scaleSlider.SetSize(XMFLOAT2(100, 20)); - scaleSlider.OnSlide([=](wi::gui::EventArgs args) { - ((wi::terrain::HeightmapModifier*)modifier)->scale = args.fValue; + amountSlider.Create(0, 1, 0.1f, 1000, "Amount: "); + amountSlider.SetSize(XMFLOAT2(100, 20)); + amountSlider.OnSlide([=](wi::gui::EventArgs args) { + ((wi::terrain::HeightmapModifier*)modifier)->amount = args.fValue; generation_callback(); }); - AddWidget(&scaleSlider); + AddWidget(&amountSlider); 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.\nIt is recommended to use a 16-bit PNG for heightmaps."); @@ -275,20 +275,20 @@ void HeightmapModifierWindow::ResizeLayout() add(blendCombo); add(weightSlider); - add(frequencySlider); - add(scaleSlider); + + add(amountSlider); add(loadButton); } void HeightmapModifierWindow::Bind(wi::terrain::HeightmapModifier* ptr) { ModifierWindow::Bind(ptr); - ptr->scale = scaleSlider.GetValue(); + ptr->amount = amountSlider.GetValue(); } void HeightmapModifierWindow::From(wi::terrain::HeightmapModifier* ptr) { ModifierWindow::From(ptr); - scaleSlider.SetValue(ptr->scale); + amountSlider.SetValue(ptr->amount); } PropWindow::PropWindow(wi::terrain::Terrain* terrain, wi::terrain::Prop* prop, wi::scene::Scene* scene) @@ -899,10 +899,10 @@ void TerrainWindow::Create(EditorComponent* _editor) bottomLevelSlider.SetValue(-60); topLevelSlider.SetValue(380); perlin->weightSlider.SetValue(0.5f); - perlin->frequencySlider.SetValue(0.0008f); + perlin->scaleSlider.SetValue(1250); perlin->octavesSlider.SetValue(6); voronoi->weightSlider.SetValue(0.5f); - voronoi->frequencySlider.SetValue(0.001f); + voronoi->scaleSlider.SetValue(1000); voronoi->fadeSlider.SetValue(2.59f); voronoi->shapeSlider.SetValue(0.7f); voronoi->falloffSlider.SetValue(6); @@ -917,10 +917,10 @@ void TerrainWindow::Create(EditorComponent* _editor) bottomLevelSlider.SetValue(-79); topLevelSlider.SetValue(520); perlin->weightSlider.SetValue(0.5f); - perlin->frequencySlider.SetValue(0.000991f); + perlin->scaleSlider.SetValue(500); perlin->octavesSlider.SetValue(6); voronoi->weightSlider.SetValue(0.5f); - voronoi->frequencySlider.SetValue(0.000317f); + voronoi->scaleSlider.SetValue(3154); voronoi->fadeSlider.SetValue(8.2f); voronoi->shapeSlider.SetValue(0.126f); voronoi->falloffSlider.SetValue(1.392f); @@ -935,17 +935,17 @@ void TerrainWindow::Create(EditorComponent* _editor) bottomLevelSlider.SetValue(0); topLevelSlider.SetValue(2960); perlin->weightSlider.SetValue(0.5f); - perlin->frequencySlider.SetValue(0.00279f); + perlin->scaleSlider.SetValue(699); perlin->octavesSlider.SetValue(8); voronoi->weightSlider.SetValue(0.5f); - voronoi->frequencySlider.SetValue(0.000496f); + voronoi->scaleSlider.SetValue(2016); voronoi->fadeSlider.SetValue(5.2f); voronoi->shapeSlider.SetValue(0.412f); voronoi->falloffSlider.SetValue(1.456f); voronoi->perturbationSlider.SetValue(0.092f); - region1Slider.SetValue(1); - region2Slider.SetValue(1); - region3Slider.SetValue(0.8f); + region1Slider.SetValue(0.7f); + region2Slider.SetValue(2); + region3Slider.SetValue(0.2f); break; case PRESET_ARCTIC: terrain->weather.SetOceanEnabled(false); @@ -953,10 +953,10 @@ void TerrainWindow::Create(EditorComponent* _editor) bottomLevelSlider.SetValue(-50); topLevelSlider.SetValue(40); perlin->weightSlider.SetValue(1); - perlin->frequencySlider.SetValue(0.002f); + perlin->scaleSlider.SetValue(500); perlin->octavesSlider.SetValue(4); voronoi->weightSlider.SetValue(1); - voronoi->frequencySlider.SetValue(0.004f); + voronoi->scaleSlider.SetValue(250); voronoi->fadeSlider.SetValue(1.8f); voronoi->shapeSlider.SetValue(0.518f); voronoi->falloffSlider.SetValue(0.2f); @@ -971,10 +971,10 @@ void TerrainWindow::Create(EditorComponent* _editor) bottomLevelSlider.SetValue(-50); topLevelSlider.SetValue(40); perlin->weightSlider.SetValue(1); - perlin->frequencySlider.SetValue(0.002f); + perlin->scaleSlider.SetValue(500); perlin->octavesSlider.SetValue(4); voronoi->weightSlider.SetValue(1); - voronoi->frequencySlider.SetValue(0.004f); + voronoi->scaleSlider.SetValue(250); voronoi->fadeSlider.SetValue(1.8f); voronoi->shapeSlider.SetValue(0.518f); voronoi->falloffSlider.SetValue(0.2f); @@ -1027,7 +1027,7 @@ void TerrainWindow::Create(EditorComponent* _editor) PerlinModifierWindow* ptr = new PerlinModifierWindow; std::shared_ptr modifier = std::make_shared(); terrain->modifiers.push_back(modifier); - ptr->Bind(modifier.get()); + ptr->From(modifier.get()); AddModifier(ptr); } break; @@ -1036,7 +1036,7 @@ void TerrainWindow::Create(EditorComponent* _editor) VoronoiModifierWindow* ptr = new VoronoiModifierWindow; std::shared_ptr modifier = std::make_shared(); terrain->modifiers.push_back(modifier); - ptr->Bind(modifier.get()); + ptr->From(modifier.get()); AddModifier(ptr); } break; @@ -1045,7 +1045,7 @@ void TerrainWindow::Create(EditorComponent* _editor) HeightmapModifierWindow* ptr = new HeightmapModifierWindow; std::shared_ptr modifier = std::make_shared(); terrain->modifiers.push_back(modifier); - ptr->Bind(modifier.get()); + ptr->From(modifier.get()); AddModifier(ptr); } break; @@ -1371,7 +1371,6 @@ void TerrainWindow::SetEntity(Entity entity) modifiers_to_remove.push_back(x.get()); } - centerToCamCheckBox.SetCheck(terrain->IsCenterToCamEnabled()); removalCheckBox.SetCheck(terrain->IsRemovalEnabled()); grassCheckBox.SetCheck(terrain->IsGrassEnabled()); @@ -1393,34 +1392,7 @@ void TerrainWindow::SetEntity(Entity entity) region2Slider.SetValue(terrain->region2); region3Slider.SetValue(terrain->region3); - auto fillMaterialCombo = [&](wi::gui::ComboBox& comboBox, Entity selected) { - comboBox.ClearItems(); - comboBox.AddItem("NO MATERIAL", INVALID_ENTITY); - for (size_t i = 0; i < scene.materials.GetCount(); ++i) - { - Entity entity = scene.materials.GetEntity(i); - if (scene.names.Contains(entity)) - { - const NameComponent& name = *scene.names.GetComponent(entity); - comboBox.AddItem(name.name, entity); - } - else - { - comboBox.AddItem(std::to_string(entity), entity); - } - - if (selected == entity) - { - comboBox.SetSelectedWithoutCallback(int(i + 1)); - } - } - }; - - for (size_t i = 0; i < arraysize(materialCombos); ++i) - { - fillMaterialCombo(materialCombos[i], terrain->materialEntities[i]); - } - fillMaterialCombo(materialCombo_GrassParticle, terrain->grassEntity); + RefreshMaterialComboBoxes(); for (auto& x : terrain->modifiers) { @@ -1455,6 +1427,38 @@ void TerrainWindow::SetEntity(Entity entity) editor->paintToolWnd.RecreateTerrainMaterialButtons(); } +void TerrainWindow::RefreshMaterialComboBoxes() +{ + wi::scene::Scene& scene = editor->GetCurrentScene(); + auto fillMaterialCombo = [&](wi::gui::ComboBox& comboBox, Entity selected) { + comboBox.ClearItems(); + comboBox.AddItem("NO MATERIAL", INVALID_ENTITY); + for (size_t i = 0; i < scene.materials.GetCount(); ++i) + { + Entity entity = scene.materials.GetEntity(i); + if (scene.names.Contains(entity)) + { + const NameComponent& name = *scene.names.GetComponent(entity); + comboBox.AddItem(name.name, entity); + } + else + { + comboBox.AddItem(std::to_string(entity), entity); + } + + if (selected == entity) + { + comboBox.SetSelectedWithoutCallback(int(i + 1)); + } + } + }; + + for (size_t i = 0; i < arraysize(materialCombos); ++i) + { + fillMaterialCombo(materialCombos[i], terrain->materialEntities[i]); + } + fillMaterialCombo(materialCombo_GrassParticle, terrain->grassEntity); +} void TerrainWindow::AddModifier(ModifierWindow* modifier_window) { modifier_window->generation_callback = [=]() { @@ -1488,6 +1492,21 @@ void TerrainWindow::SetupAssets() { terrain_preset.materialEntities[i] = CreateEntity(); currentScene.materials.Create(terrain_preset.materialEntities[i]); + switch (i) + { + case wi::terrain::MATERIAL_BASE: + currentScene.names.Create(terrain_preset.materialEntities[i]) = "Base"; + break; + case wi::terrain::MATERIAL_SLOPE: + currentScene.names.Create(terrain_preset.materialEntities[i]) = "Slope"; + break; + case wi::terrain::MATERIAL_LOW_ALTITUDE: + currentScene.names.Create(terrain_preset.materialEntities[i]) = "LowAltitude"; + break; + case wi::terrain::MATERIAL_HIGH_ALTITUDE: + currentScene.names.Create(terrain_preset.materialEntities[i]) = "HighAltitude"; + break; + } currentScene.Component_Attach(terrain_preset.materialEntities[i], entity); } @@ -1833,6 +1852,8 @@ void TerrainWindow::SetupAssets() presetCombo.SetSelected(0); editor->paintToolWnd.RecreateTerrainMaterialButtons(); + + RefreshMaterialComboBoxes(); } void TerrainWindow::Update(const wi::Canvas& canvas, float dt) diff --git a/Editor/TerrainWindow.h b/Editor/TerrainWindow.h index 31ece1dc5..e13348b16 100644 --- a/Editor/TerrainWindow.h +++ b/Editor/TerrainWindow.h @@ -7,7 +7,7 @@ struct ModifierWindow : public wi::gui::Window std::function generation_callback; wi::gui::ComboBox blendCombo; wi::gui::Slider weightSlider; - wi::gui::Slider frequencySlider; + wi::gui::Slider scaleSlider; virtual ~ModifierWindow() = default; @@ -38,7 +38,7 @@ struct VoronoiModifierWindow : public ModifierWindow }; struct HeightmapModifierWindow : public ModifierWindow { - wi::gui::Slider scaleSlider; + wi::gui::Slider amountSlider; wi::gui::Button loadButton; HeightmapModifierWindow(); @@ -149,6 +149,8 @@ public: void AddModifier(ModifierWindow* modifier_window); void SetupAssets(); + void RefreshMaterialComboBoxes(); + void Update(const wi::Canvas& canvas, float dt) override; void ResizeLayout() override; }; diff --git a/WickedEngine/wiGraphicsDevice_DX12.cpp b/WickedEngine/wiGraphicsDevice_DX12.cpp index a20a205fa..6cf43e2f9 100644 --- a/WickedEngine/wiGraphicsDevice_DX12.cpp +++ b/WickedEngine/wiGraphicsDevice_DX12.cpp @@ -6667,6 +6667,8 @@ std::mutex queue_locker; commandlist.GetGraphicsCommandList()->IASetPrimitiveTopology(internal_state->primitiveTopology); } } + else + return; // early exit for static pso commandlist.prev_pipeline_hash = {}; commandlist.dirty_pso = false; @@ -6678,7 +6680,8 @@ std::mutex queue_locker; pipeline_hash.renderpass_hash = commandlist.renderpass_info.get_hash(); if (commandlist.prev_pipeline_hash == pipeline_hash) { - return; + commandlist.active_pso = pso; + return; // early exit for dynamic pso|renderpass } commandlist.prev_pipeline_hash = pipeline_hash; commandlist.dirty_pso = true; @@ -6700,9 +6703,7 @@ std::mutex queue_locker; { CommandList_DX12& commandlist = GetCommandList(cmd); if (commandlist.active_cs == cs) - { return; - } commandlist.active_pso = nullptr; commandlist.active_rt = nullptr; diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index 36004122a..3fd0055dc 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -8257,8 +8257,6 @@ using namespace vulkan_internal; void GraphicsDevice_Vulkan::BindPipelineState(const PipelineState* pso, CommandList cmd) { CommandList_Vulkan& commandlist = GetCommandList(cmd); - if (commandlist.active_pso == pso) - return; commandlist.active_cs = nullptr; commandlist.active_rt = nullptr; @@ -8270,6 +8268,8 @@ using namespace vulkan_internal; { vkCmdBindPipeline(commandlist.GetCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, internal_state->pipeline); } + else + return; // early exit for static pso commandlist.prev_pipeline_hash = {}; commandlist.dirty_pso = false; @@ -8282,7 +8282,7 @@ using namespace vulkan_internal; if (commandlist.prev_pipeline_hash == pipeline_hash) { commandlist.active_pso = pso; - return; + return; // early exit for dynamic pso|renderpass } commandlist.prev_pipeline_hash = pipeline_hash; commandlist.dirty_pso = true; diff --git a/WickedEngine/wiNoise.h b/WickedEngine/wiNoise.h index 755563348..823fd9699 100644 --- a/WickedEngine/wiNoise.h +++ b/WickedEngine/wiNoise.h @@ -1,8 +1,11 @@ #pragma once #include "CommonInclude.h" -#include "wiMath.h" #include "wiArchive.h" #include "wiRandom.h" +#include "wiMath.h" + +// Note: these should be implemented independently of math library optimizations to be cross platform deterministic! +// Otherwise the terrain generation might be different across platforms namespace wi::noise { @@ -119,52 +122,124 @@ namespace wi::noise // Based on: https://www.shadertoy.com/view/MslGD8 namespace voronoi { - inline XMVECTOR fract(XMVECTOR p) + constexpr float dot(XMFLOAT2 a, XMFLOAT2 b) { - return p - XMVectorFloor(p); + return a.x * b.x + a.y * b.y; } - inline XMVECTOR hash(XMVECTOR p) + inline XMFLOAT2 fract(XMFLOAT2 p) { - p = XMVectorSet( - XMVectorGetX(XMVector2Dot(p, XMVectorSet(127.1f, 311.7f, 0, 0))), - XMVectorGetX(XMVector2Dot(p, XMVectorSet(269.5f, 183.3f, 0, 0))), - 0, - 0 + return XMFLOAT2( + p.x - std::floor(p.x), + p.y - std::floor(p.y) ); - return fract(XMVectorSin(p) * 18.5453f); + } + inline XMFLOAT2 floor(XMFLOAT2 p) + { + return XMFLOAT2( + std::floor(p.x), + std::floor(p.y) + ); + } + + // Backwards compatibility implementation for XMVectorSin() without FMA instruction: + inline XMVECTOR XM_CALLCONV FMADD_COMPAT(XMVECTOR a, XMVECTOR b, XMVECTOR c) noexcept { return _mm_add_ps(_mm_mul_ps((a), (b)), (c)); } + inline XMVECTOR XM_CALLCONV FNMADD_COMPAT(XMVECTOR a, XMVECTOR b, XMVECTOR c) noexcept { return _mm_sub_ps((c), _mm_mul_ps((a), (b))); } + inline XMVECTOR XM_CALLCONV XMVectorModAngles_COMPAT(FXMVECTOR Angles) noexcept + { + // Modulo the range of the given angles such that -XM_PI <= Angles < XM_PI + XMVECTOR vResult = _mm_mul_ps(Angles, g_XMReciprocalTwoPi); + // Use the inline function due to complexity for rounding + vResult = XMVectorRound(vResult); + return FNMADD_COMPAT(vResult, g_XMTwoPi, Angles); + } + inline XMFLOAT2 sin(XMFLOAT2 p) + { + XMVECTOR P = XMLoadFloat2(&p); + //P = XMVectorSin(P); + { + // Force the value within the bounds of pi + XMVECTOR x = XMVectorModAngles_COMPAT(P); + + // Map in [-pi/2,pi/2] with sin(y) = sin(x). + __m128 sign = _mm_and_ps(x, g_XMNegativeZero); + __m128 c = _mm_or_ps(g_XMPi, sign); // pi when x >= 0, -pi when x < 0 + __m128 absx = _mm_andnot_ps(sign, x); // |x| + __m128 rflx = _mm_sub_ps(c, x); + __m128 comp = _mm_cmple_ps(absx, g_XMHalfPi); + __m128 select0 = _mm_and_ps(comp, x); + __m128 select1 = _mm_andnot_ps(comp, rflx); + x = _mm_or_ps(select0, select1); + + __m128 x2 = _mm_mul_ps(x, x); + + // Compute polynomial approximation + const XMVECTOR SC1 = g_XMSinCoefficients1; + __m128 vConstantsB = XM_PERMUTE_PS(SC1, _MM_SHUFFLE(0, 0, 0, 0)); + const XMVECTOR SC0 = g_XMSinCoefficients0; + __m128 vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 Result = FMADD_COMPAT(vConstantsB, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(2, 2, 2, 2)); + Result = FMADD_COMPAT(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(1, 1, 1, 1)); + Result = FMADD_COMPAT(Result, x2, vConstants); + + vConstants = XM_PERMUTE_PS(SC0, _MM_SHUFFLE(0, 0, 0, 0)); + Result = FMADD_COMPAT(Result, x2, vConstants); + + Result = FMADD_COMPAT(Result, x2, g_XMOne); + Result = _mm_mul_ps(Result, x); + + P = Result; + } + XMFLOAT2 ret; + XMStoreFloat2(&ret, P); + return ret; + } + + inline XMFLOAT2 hash(XMFLOAT2 p) + { + XMFLOAT2 ret = XMFLOAT2( + dot(XMFLOAT2(p.x, p.y), XMFLOAT2(127.1f, 311.7f)), + dot(XMFLOAT2(p.x, p.y), XMFLOAT2(269.5f, 183.3f)) + ); + ret = sin(ret); + return fract(XMFLOAT2(ret.x * 18.5453f, ret.y * 18.5453f)); } struct Result { - float distance; - float cell_id; + float distance = 0; + float cell_id = 0; }; inline Result compute(float x, float y, float seed) { - Result result = {}; + Result result; - XMVECTOR p = XMVectorSet(x, y, 0, 0); - XMVECTOR n = XMVectorFloor(p); - XMVECTOR f = fract(p); + XMFLOAT2 p = XMFLOAT2(x, y); + XMFLOAT2 n = floor(p); + XMFLOAT2 f = fract(p); - XMVECTOR m = XMVectorSet(8, 0, 0, 0); + XMFLOAT3 m = XMFLOAT3(8, 0, 0); for (int j = -1; j <= 1; j++) { for (int i = -1; i <= 1; i++) { - XMVECTOR g = XMVectorSet(float(i), float(j), 0, 0); - XMVECTOR o = hash(n + g); - //XMVECTOR r = g - f + o; - XMVECTOR r = g - f + (XMVectorReplicate(0.5f) + 0.5f * XMVectorSin(seed * o)); - float d = XMVectorGetX(XMVector2Dot(r, r)); - if (d < XMVectorGetX(m)) + XMFLOAT2 g = XMFLOAT2(float(i), float(j)); + XMFLOAT2 o = hash(XMFLOAT2(n.x + g.x, n.y + g.y)); + XMFLOAT2 r; + r.x = g.x - f.x + (0.5f + 0.5f * std::sin(seed * o.x)); + r.y = g.y - f.y + (0.5f + 0.5f * std::sin(seed * o.y)); + float d = dot(r, r); + if (d < m.x) { - m = XMVectorSet(d, XMVectorGetX(o), XMVectorGetY(o), 0); + m = XMFLOAT3(d, o.x, o.y); } } } - result.distance = XMVectorGetX(XMVectorSqrt(m)); - result.cell_id = XMVectorGetY(m) + XMVectorGetZ(m); + result.distance = std::sqrt(m.x); + result.cell_id = m.y + m.z; return result; } diff --git a/WickedEngine/wiRandom.h b/WickedEngine/wiRandom.h index bc9f67e45..e0d5e4745 100644 --- a/WickedEngine/wiRandom.h +++ b/WickedEngine/wiRandom.h @@ -22,9 +22,9 @@ namespace wi::random // gives an uint in range [0, UINT64_MAX] constexpr uint64_t next_uint() { - state ^= state >> 12; - state ^= state << 25; - state ^= state >> 27; + state ^= state >> 12ull; + state ^= state << 25ull; + state ^= state >> 27ull; return state * 0x2545F4914F6CDD1DULL; } // gives an uint64 in range [min, max] @@ -76,7 +76,7 @@ namespace wi::random uint32_t u; float f; } value = {}; - value.u = 0x3f800000 | (uint32_t(next_uint()) >> 9); + value.u = 0x3f800000u | (uint32_t(next_uint()) >> 9); return value.f - 1.0f; } // gives a float in range [min, max] diff --git a/WickedEngine/wiTerrain.cpp b/WickedEngine/wiTerrain.cpp index 7a80ddb55..b095f723d 100644 --- a/WickedEngine/wiTerrain.cpp +++ b/WickedEngine/wiTerrain.cpp @@ -498,26 +498,6 @@ namespace wi::terrain { scene->materials.Create(materialEntities[i]); } - if (i < MATERIAL_COUNT && !scene->names.Contains(materialEntities[i])) - { - NameComponent& name = scene->names.Create(materialEntities[i]); - switch (i) - { - default: - case MATERIAL_BASE: - name = "material_base"; - break; - case MATERIAL_SLOPE: - name = "material_slope"; - break; - case MATERIAL_LOW_ALTITUDE: - name = "material_low_altitude"; - break; - case MATERIAL_HIGH_ALTITUDE: - name = "material_high_altitude"; - break; - } - } *scene->materials.GetComponent(materialEntities[i]) = materials[i]; } } @@ -1189,9 +1169,9 @@ namespace wi::terrain { if (prop.data.empty()) continue; - const int gen_count = (int)rng.next_uint( - uint32_t(prop.min_count_per_chunk * chunk_data.prop_density_current), - std::max(1u, uint32_t(prop.max_count_per_chunk * chunk_data.prop_density_current)) + const int gen_count = rng.next_int( + int(std::floor(float(prop.min_count_per_chunk) * chunk_data.prop_density_current)), + int(std::ceil(float(prop.max_count_per_chunk) * chunk_data.prop_density_current)) ); for (int i = 0; i < gen_count; ++i) { @@ -1205,6 +1185,9 @@ namespace wi::terrain XMFLOAT4 region0 = wi::Color(chunk_data.blendmap_layers[0].pixels[ind0], chunk_data.blendmap_layers[1].pixels[ind0], chunk_data.blendmap_layers[2].pixels[ind0], chunk_data.blendmap_layers[3].pixels[ind0]); XMFLOAT4 region1 = wi::Color(chunk_data.blendmap_layers[0].pixels[ind1], chunk_data.blendmap_layers[1].pixels[ind1], chunk_data.blendmap_layers[2].pixels[ind1], chunk_data.blendmap_layers[3].pixels[ind1]); XMFLOAT4 region2 = wi::Color(chunk_data.blendmap_layers[0].pixels[ind2], chunk_data.blendmap_layers[1].pixels[ind2], chunk_data.blendmap_layers[2].pixels[ind2], chunk_data.blendmap_layers[3].pixels[ind2]); + weight_norm(region0); + weight_norm(region1); + weight_norm(region2); float spline_factor0 = 0; float spline_factor1 = 0; float spline_factor2 = 0; @@ -1221,9 +1204,6 @@ namespace wi::terrain spline_factor1 *= rcp; spline_factor2 *= rcp; } - weight_norm(region0); - weight_norm(region1); - weight_norm(region2); // random barycentric coords on the triangle: float f = rng.next_float(); float g = rng.next_float(); @@ -2305,7 +2285,7 @@ namespace wi::terrain { std::shared_ptr modifier = std::make_shared(); modifiers[i] = modifier; - archive >> modifier->scale; + archive >> modifier->amount; archive >> modifier->data; archive >> modifier->width; archive >> modifier->height; @@ -2422,7 +2402,7 @@ namespace wi::terrain ((VoronoiModifier*)modifier.get())->perlin_noise.Serialize(archive); break; case Modifier::Type::Heightmap: - archive << ((HeightmapModifier*)modifier.get())->scale; + archive << ((HeightmapModifier*)modifier.get())->amount; archive << ((HeightmapModifier*)modifier.get())->data; archive << ((HeightmapModifier*)modifier.get())->width; archive << ((HeightmapModifier*)modifier.get())->height; diff --git a/WickedEngine/wiTerrain.h b/WickedEngine/wiTerrain.h index cec104d82..1fae0e2d1 100644 --- a/WickedEngine/wiTerrain.h +++ b/WickedEngine/wiTerrain.h @@ -18,7 +18,7 @@ namespace wi::terrain { struct { - int x, z; + int32_t x, z; }; uint64_t raw = 0; }; @@ -383,6 +383,10 @@ namespace wi::terrain float weight = 0.5f; float frequency = 0.0008f; + // helpers for more user friendly setup with scaling in world space: + constexpr void SetScale(float scale) { frequency = 1.0f / scale; } + constexpr float GetScale() const { return 1.0f / frequency; } + virtual void Seed(uint32_t seed) {} virtual void Apply(const XMFLOAT2& world_pos, float& height) = 0; constexpr void Blend(float& height, float value) @@ -455,13 +459,13 @@ namespace wi::terrain }; struct HeightmapModifier : public Modifier { - float scale = 0.1f; + float amount = 0.1f; // multiplier for height values wi::vector data; int width = 0; int height = 0; - HeightmapModifier() { type = Type::Heightmap; } + HeightmapModifier() { type = Type::Heightmap; SetScale(1.0f); } void Apply(const XMFLOAT2& world_pos, float& height) override { XMFLOAT2 p = world_pos; @@ -480,7 +484,7 @@ namespace wi::terrain { value = ((float)((uint16_t*)data.data())[idx] / 65535.0f); } - Blend(height, value * scale); + Blend(height, value * amount); } } }; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 5ab44be5a..3de655d33 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 = 751; + const int revision = 752; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);