diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index c33054b3a..2bfa70056 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -1858,6 +1858,7 @@ void EditorComponent::Update(float dt) { for (size_t i = 0; i < scene.splines.GetCount(); ++i) { + //wi::renderer::DrawBox(XMMatrixTranslationFromVector(scene.splines[i].ClosestPointOnSpline(camera.GetEye()))); for (size_t j = 0; j < scene.splines[i].spline_node_transforms.size(); ++j) { const TransformComponent& transform = scene.splines[i].spline_node_transforms[j]; diff --git a/Editor/SplineWindow.cpp b/Editor/SplineWindow.cpp index 0bd6292a2..1347b3e9c 100644 --- a/Editor/SplineWindow.cpp +++ b/Editor/SplineWindow.cpp @@ -26,8 +26,8 @@ void SplineWindow::Create(EditorComponent* _editor) }); infoLabel.Create("SplineInfo"); - infoLabel.SetSize(XMFLOAT2(100, 90)); - infoLabel.SetText("The spline is a curve that goes through every specified entity that has a TransformComponent smoothly. A mesh can be generated from it automatically by increasing the subdivisions."); + infoLabel.SetSize(XMFLOAT2(100, 120)); + infoLabel.SetText("The spline is a curve that goes through every specified entity that has a TransformComponent smoothly. A mesh can be generated from it automatically by increasing the subdivisions. It can also modify terrain when the terrain modification slider is used."); AddWidget(&infoLabel); loopedCheck.Create("Looped: "); @@ -159,6 +159,28 @@ void SplineWindow::Create(EditorComponent* _editor) }); AddWidget(&subdivVerticalSlider); + terrainSlider.Create(0, 1, 0, 100, "Terrain modifier: "); + terrainSlider.SetTooltip("Set terrain modification strength (0 to turn it off, higher values mean more sharpness)."); + terrainSlider.OnSlide([this](wi::gui::EventArgs args) { + wi::scene::Scene& scene = editor->GetCurrentScene(); + for (auto& x : editor->translator.selected) + { + SplineComponent* spline = scene.splines.GetComponent(x.entity); + if (spline == nullptr) + continue; + spline->terrain_modifier_amount = args.fValue; + } + + // indirect set: + SplineComponent* spline = scene.splines.GetComponent(entity); + if (spline != nullptr) + { + spline->terrain_modifier_amount = args.fValue; + } + editor->componentsWnd.RefreshEntityTree(); + }); + AddWidget(&terrainSlider); + addButton.Create("AddNode"); addButton.SetText("+"); addButton.SetTooltip("Add an entity as a node to the spline (it must have TransformComponent). Hotkey: Ctrl + E"); @@ -191,6 +213,7 @@ void SplineWindow::SetEntity(Entity entity) rotSlider.SetValue(wi::math::RadiansToDegrees(spline->rotation)); subdivSlider.SetValue(spline->mesh_generation_subdivision); subdivVerticalSlider.SetValue(spline->mesh_generation_vertical_subdivision); + terrainSlider.SetValue(spline->terrain_modifier_amount); if (changed) { @@ -330,6 +353,7 @@ void SplineWindow::ResizeLayout() add_fullwidth(infoLabel); add(subdivSlider); add(subdivVerticalSlider); + add(terrainSlider); add(widthSlider); add(rotSlider); add_right(loopedCheck); diff --git a/Editor/SplineWindow.h b/Editor/SplineWindow.h index e0b680e99..c21e18aff 100644 --- a/Editor/SplineWindow.h +++ b/Editor/SplineWindow.h @@ -17,6 +17,7 @@ public: wi::gui::Slider rotSlider; wi::gui::Slider subdivSlider; wi::gui::Slider subdivVerticalSlider; + wi::gui::Slider terrainSlider; wi::gui::Button addButton; struct Entry diff --git a/Editor/Translator.cpp b/Editor/Translator.cpp index 0a4f5b2ae..4c8296546 100644 --- a/Editor/Translator.cpp +++ b/Editor/Translator.cpp @@ -408,22 +408,22 @@ void Translator::Update(const CameraComponent& camera, const XMFLOAT4& currentMo if (state == TRANSLATOR_X) { XMVECTOR A = pos, B = pos + XMVectorSet(1, 0, 0, 0); - XMVECTOR P = wi::math::GetClosestPointToLine(A, B, intersection); - XMVECTOR PPrev = wi::math::GetClosestPointToLine(A, B, intersectionPrev); + XMVECTOR P = wi::math::ClosestPointOnLine(A, B, intersection); + XMVECTOR PPrev = wi::math::ClosestPointOnLine(A, B, intersectionPrev); deltaV = P - PPrev; } else if (state == TRANSLATOR_Y) { XMVECTOR A = pos, B = pos + XMVectorSet(0, 1, 0, 0); - XMVECTOR P = wi::math::GetClosestPointToLine(A, B, intersection); - XMVECTOR PPrev = wi::math::GetClosestPointToLine(A, B, intersectionPrev); + XMVECTOR P = wi::math::ClosestPointOnLine(A, B, intersection); + XMVECTOR PPrev = wi::math::ClosestPointOnLine(A, B, intersectionPrev); deltaV = P - PPrev; } else if (state == TRANSLATOR_Z) { XMVECTOR A = pos, B = pos + XMVectorSet(0, 0, 1, 0); - XMVECTOR P = wi::math::GetClosestPointToLine(A, B, intersection); - XMVECTOR PPrev = wi::math::GetClosestPointToLine(A, B, intersectionPrev); + XMVECTOR P = wi::math::ClosestPointOnLine(A, B, intersection); + XMVECTOR PPrev = wi::math::ClosestPointOnLine(A, B, intersectionPrev); deltaV = P - PPrev; } else diff --git a/WickedEngine/CommonInclude.h b/WickedEngine/CommonInclude.h index 5d062b7d8..7abc7df22 100644 --- a/WickedEngine/CommonInclude.h +++ b/WickedEngine/CommonInclude.h @@ -19,6 +19,9 @@ template constexpr T sqr(T x) { return x * x; } +template +constexpr T pow4(T x) { return x * x * x * x; } + template constexpr T clamp(T x, T a, T b) { diff --git a/WickedEngine/wiMath.cpp b/WickedEngine/wiMath.cpp index 1df154cbb..c4cfac10a 100644 --- a/WickedEngine/wiMath.cpp +++ b/WickedEngine/wiMath.cpp @@ -61,24 +61,6 @@ namespace wi::math return XMFLOAT3(roll, pitch, yaw); } - XMVECTOR GetClosestPointToLine(const XMVECTOR& A, const XMVECTOR& B, const XMVECTOR& P, bool segmentClamp) - { - XMVECTOR AP_ = P - A; - XMVECTOR AB_ = B - A; - XMFLOAT3 AB, AP; - XMStoreFloat3(&AB, AB_); - XMStoreFloat3(&AP, AP_); - float ab2 = AB.x*AB.x + AB.y*AB.y + AB.z*AB.z; - float ap_ab = AP.x*AB.x + AP.y*AB.y + AP.z*AB.z; - float t = ap_ab / ab2; - if (segmentClamp) - { - if (t < 0.0f) t = 0.0f; - else if (t > 1.0f) t = 1.0f; - } - XMVECTOR Closest = A + AB_ * t; - return Closest; - } float GetPointSegmentDistance(const XMVECTOR& point, const XMVECTOR& segmentA, const XMVECTOR& segmentB) { // Return minimum distance between line segment vw and point p diff --git a/WickedEngine/wiMath.h b/WickedEngine/wiMath.h index 7a261a4ef..260ab963a 100644 --- a/WickedEngine/wiMath.h +++ b/WickedEngine/wiMath.h @@ -399,7 +399,6 @@ namespace wi::math XMFLOAT3 QuaternionToRollPitchYaw(const XMFLOAT4& quaternion); - XMVECTOR GetClosestPointToLine(const XMVECTOR& A, const XMVECTOR& B, const XMVECTOR& P, bool segmentClamp = false); float GetPointSegmentDistance(const XMVECTOR& point, const XMVECTOR& segmentA, const XMVECTOR& segmentB); inline float GetPlanePointDistance(const XMVECTOR& planeOrigin, const XMVECTOR& planeNormal, const XMVECTOR& point) @@ -434,6 +433,35 @@ namespace wi::math return XMFLOAT3(_m.m[0][0], _m.m[0][1], _m.m[0][2]); } + inline XMVECTOR GetPosition(const XMMATRIX& M) + { + XMFLOAT4X4 _m; + XMStoreFloat4x4(&_m, M); + XMFLOAT3 ret = GetPosition(_m); + return XMLoadFloat3(&ret); + } + inline XMVECTOR GetForward(const XMMATRIX& M) + { + XMFLOAT4X4 _m; + XMStoreFloat4x4(&_m, M); + XMFLOAT3 ret = GetForward(_m); + return XMLoadFloat3(&ret); + } + inline XMVECTOR GetUp(const XMMATRIX& M) + { + XMFLOAT4X4 _m; + XMStoreFloat4x4(&_m, M); + XMFLOAT3 ret = GetUp(_m); + return XMLoadFloat3(&ret); + } + inline XMVECTOR GetRight(const XMMATRIX& M) + { + XMFLOAT4X4 _m; + XMStoreFloat4x4(&_m, M); + XMFLOAT3 ret = GetRight(_m); + return XMLoadFloat3(&ret); + } + // Returns an element of a precomputed halton sequence. Specify which iteration to get with idx >= 0 const XMFLOAT4& GetHaltonSequence(int idx); diff --git a/WickedEngine/wiPrimitive.cpp b/WickedEngine/wiPrimitive.cpp index 2f57e0114..d873a2864 100644 --- a/WickedEngine/wiPrimitive.cpp +++ b/WickedEngine/wiPrimitive.cpp @@ -139,6 +139,12 @@ namespace wi::primitive if (p.z < _min.z) return false; return true; } + bool AABB::intersects(const XMVECTOR& P) const + { + XMFLOAT3 p; + XMStoreFloat3(&p, P); + return intersects(p); + } bool AABB::intersects(const Ray& ray) const { if (!IsValid()) @@ -210,6 +216,12 @@ namespace wi::primitive _min = wi::math::Min(_min, pos); _max = wi::math::Max(_max, pos); } + void AABB::AddPoint(const XMVECTOR& P) + { + XMFLOAT3 p; + XMStoreFloat3(&p, P); + AddPoint(p); + } void AABB::Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri) { if (archive.IsReadMode()) diff --git a/WickedEngine/wiPrimitive.h b/WickedEngine/wiPrimitive.h index 3249da92f..8631bf3a0 100644 --- a/WickedEngine/wiPrimitive.h +++ b/WickedEngine/wiPrimitive.h @@ -45,12 +45,14 @@ namespace wi::primitive INTERSECTION_TYPE intersects2D(const AABB& b) const; INTERSECTION_TYPE intersects(const AABB& b) const; bool intersects(const XMFLOAT3& p) const; + bool intersects(const XMVECTOR& P) const; bool intersects(const Ray& ray) const; bool intersects(const Sphere& sphere) const; bool intersects(const BoundingFrustum& frustum) const; AABB operator* (float a); static AABB Merge(const AABB& a, const AABB& b); void AddPoint(const XMFLOAT3& pos); + void AddPoint(const XMVECTOR& P); // projects the AABB to the screen, returns a 2D rectangle in UV-space as Vector(topleftX, topleftY, bottomrightX, bottomrightY), each value is in range [0, 1] XMFLOAT4 ProjectToScreen(const XMMATRIX& ViewProjection) const; diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index a56ed82dc..59922e1df 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -5781,6 +5781,9 @@ namespace wi::scene dirty |= spline.prev_looped != spline.IsLooped(); dirty |= spline.prev_width != spline.width; dirty |= spline.prev_rotation != spline.rotation; + dirty |= spline.prev_terrain_modifier_amount != spline.terrain_modifier_amount; + + spline.prev_terrain_modifier_amount = spline.terrain_modifier_amount; spline.spline_node_transforms.resize(spline.spline_node_entities.size()); @@ -5800,6 +5803,8 @@ namespace wi::scene } } + spline.SetDirty(dirty); + // Mesh generation: if (dirty && spline.spline_node_transforms.size() > 1) { @@ -6005,6 +6010,13 @@ namespace wi::scene XMStoreFloat4x4(&spline.spline_node_transforms[i].world, ComputeEntityMatrixRecursive(node_entity)); } } + + // Compute AABB: + if (dirty) + { + spline.aabb = spline.ComputeAABB(10); + spline.dirty_terrain = true; + } }); wi::jobsystem::Wait(ctx); } diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 48bc311ca..7c327f3e0 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -64,7 +64,7 @@ namespace wi::scene wi::ecs::ComponentManager& metadatas = componentLibrary.Register("wi::scene::Scene::metadatas"); wi::ecs::ComponentManager& characters = componentLibrary.Register("wi::scene::Scene::characters"); wi::ecs::ComponentManager& constraints = componentLibrary.Register("wi::scene::Scene::constraints", 5); // version = 5 - wi::ecs::ComponentManager& splines = componentLibrary.Register("wi::scene::Scene::splines"); + wi::ecs::ComponentManager& splines = componentLibrary.Register("wi::scene::Scene::splines", 1); // version = 1 // Non-serialized attributes: float dt = 0; diff --git a/WickedEngine/wiScene_Components.cpp b/WickedEngine/wiScene_Components.cpp index 126d7c1bd..9c44d3991 100644 --- a/WickedEngine/wiScene_Components.cpp +++ b/WickedEngine/wiScene_Components.cpp @@ -2859,7 +2859,7 @@ namespace wi::scene return active; } - XMMATRIX SplineComponent::EvaluateSplineAt(float t) + XMMATRIX SplineComponent::EvaluateSplineAt(float t) const { if (spline_node_transforms.empty()) return {}; @@ -2988,4 +2988,89 @@ namespace wi::scene XMMATRIX M = { B, N, T, P }; return M; } + XMVECTOR SplineComponent::ClosestPointOnSpline(const XMVECTOR& P, int steps) const + { + if (spline_node_transforms.empty()) + return XMVectorZero(); + if (spline_node_transforms.size() == 1) + return spline_node_transforms[0].GetPositionV(); + + if (spline_node_transforms.size() == 2) + return wi::math::ClosestPointOnLineSegment(spline_node_transforms[0].GetPositionV(), spline_node_transforms[1].GetPositionV(), P); + + steps *= (int)spline_node_transforms.size(); + float mindist = FLT_MAX; + XMVECTOR MIN = XMVectorZero(); + XMVECTOR A = wi::math::GetPosition(EvaluateSplineAt(0.0f)); + for (int i = 1; i < steps; ++i) + { + const float t = float(i) / float(steps - 1); + XMMATRIX M = EvaluateSplineAt(t); + XMVECTOR B = wi::math::GetPosition(M); + XMVECTOR C = wi::math::ClosestPointOnLineSegment(A, B, P); + float dist = wi::math::Distance(P, C); + if (dist < mindist) + { + mindist = dist; + MIN = C; + } + A = B; + } + + return MIN; + } + XMVECTOR SplineComponent::TraceSplinePlane(const XMVECTOR& ORIGIN, const XMVECTOR& DIRECTION, int steps) const + { + if (spline_node_transforms.empty()) + return XMVectorZero(); + if (spline_node_transforms.size() == 1) + return XMVectorZero(); + + steps *= (int)spline_node_transforms.size(); + float mindist = FLT_MAX; + XMVECTOR MIN = XMVectorZero(); + for (int i = 0; i < steps; ++i) + { + const float t = float(i) / float(steps - 1); + XMMATRIX M = EvaluateSplineAt(t); + XMVECTOR P = wi::math::GetPosition(M); + XMVECTOR N = wi::math::GetUp(M); + XMVECTOR PLANE = XMPlaneFromPointNormal(P, N); + XMVECTOR I = XMPlaneIntersectLine(PLANE, ORIGIN, ORIGIN + DIRECTION * 100000); + float dist = wi::math::Distance(P, I); + if (dist < mindist) + { + mindist = dist; + MIN = I; + } + } + + return MIN; + } + AABB SplineComponent::ComputeAABB(int steps) const + { + AABB ret; + float range = width; + if (terrain_modifier_amount > 0) + { + range /= sqr(terrain_modifier_amount); + } + steps *= (int)spline_node_transforms.size(); + for (int i = 0; i < steps; ++i) + { + const float t = float(i) / float(steps - 1); + XMMATRIX M = EvaluateSplineAt(t); + XMVECTOR P = wi::math::GetPosition(M); + XMVECTOR R = XMVector3Normalize(wi::math::GetRight(M)) * range; + XMVECTOR N = XMVector3Normalize(wi::math::GetUp(M)) * range; + XMVECTOR F = XMVector3Normalize(wi::math::GetForward(M)) * range; + ret.AddPoint(P - R); + ret.AddPoint(P + R); + ret.AddPoint(P - N); + ret.AddPoint(P + N); + ret.AddPoint(P + F); + ret.AddPoint(P - F); + } + return ret; + } } diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index 4fd770dba..8d955f266 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -2544,6 +2544,7 @@ namespace wi::scene NONE = 0, DRAW_ALIGNED = 1 << 0, LOOPED = 1 << 1, + DIRTY = 1 << 2, }; uint32_t _flags = NONE; @@ -2551,6 +2552,7 @@ namespace wi::scene float rotation = 0; // rotation of nodes in radians around the spline axis (affects mesh generation) int mesh_generation_subdivision = 0; // increase this above 0 to request mesh generation int mesh_generation_vertical_subdivision = 0; // can create vertically subdivided mesh (corridoor, tunnel, etc. with this) + float terrain_modifier_amount = 0; // increase above 0 to affect terrain generation wi::vector spline_node_entities; @@ -2561,11 +2563,19 @@ namespace wi::scene int prev_mesh_generation_subdivision = 0; int prev_mesh_generation_vertical_subdivision = 0; int prev_mesh_generation_nodes = 0; + float prev_terrain_modifier_amount = 0; bool prev_looped = false; + wi::primitive::AABB aabb; + mutable bool dirty_terrain = false; // Evaluate an interpolated location on the spline at t which in range [0,1] on the spline // the result matrix is oriented to look towards the spline direction and face upwards along the spline normal - XMMATRIX EvaluateSplineAt(float t); + XMMATRIX EvaluateSplineAt(float t) const; + + XMVECTOR ClosestPointOnSpline(const XMVECTOR& P, int steps = 10) const; + XMVECTOR TraceSplinePlane(const XMVECTOR& ORIGIN, const XMVECTOR& DIRECTION, int steps = 10) const; + + wi::primitive::AABB ComputeAABB(int steps = 10) const; // By default the spline is drawn as camera facing, this can be used to set it to be drawn aligned to segment rotations: bool IsDrawAligned() const { return _flags & DRAW_ALIGNED; } @@ -2574,6 +2584,9 @@ namespace wi::scene bool IsLooped() const { return _flags & LOOPED; } void SetLooped(bool value = true) { if (value) { _flags |= LOOPED; } else { _flags &= ~LOOPED; } } + bool IsDirty() const { return _flags & DIRTY; } + void SetDirty(bool value = true) { if (value) { _flags |= DIRTY; } else { _flags &= ~DIRTY; } } + void Serialize(wi::Archive& archive, wi::ecs::EntitySerializer& seri); }; } diff --git a/WickedEngine/wiScene_Serializers.cpp b/WickedEngine/wiScene_Serializers.cpp index 60fc95b69..7f8526e2d 100644 --- a/WickedEngine/wiScene_Serializers.cpp +++ b/WickedEngine/wiScene_Serializers.cpp @@ -2512,6 +2512,11 @@ namespace wi::scene { SerializeEntity(archive, spline_node_entities[i], seri); } + + if (seri.GetVersion() >= 1) + { + archive >> terrain_modifier_amount; + } } else { @@ -2526,6 +2531,11 @@ namespace wi::scene { SerializeEntity(archive, spline_node_entities[i], seri); } + + if (seri.GetVersion() >= 1) + { + archive << terrain_modifier_amount; + } } } diff --git a/WickedEngine/wiTerrain.cpp b/WickedEngine/wiTerrain.cpp index 327b9da8c..ddcd4e8cd 100644 --- a/WickedEngine/wiTerrain.cpp +++ b/WickedEngine/wiTerrain.cpp @@ -182,6 +182,7 @@ namespace wi::terrain wi::scene::Scene scene; // The background generation thread can safely add things to this, it will be merged into the main scene when it is safe to do so wi::jobsystem::context workload; std::atomic_bool cancelled{ false }; + wi::vector splines; }; wi::jobsystem::context virtual_texture_ctx; @@ -593,6 +594,12 @@ namespace wi::terrain break; } } + for (size_t i = 0; i < scene->splines.GetCount(); ++i) + { + const SplineComponent& spline = scene->splines[i]; + restart_generation |= spline.dirty_terrain; + spline.dirty_terrain = false; + } if (restart_generation) { @@ -856,6 +863,15 @@ namespace wi::terrain // Start the generation on a background thread and keep it running until the next frame generator->cancelled.store(false); generator->workload.priority = wi::jobsystem::Priority::Low; + generator->splines.clear(); + for (size_t i = 0; i < scene->splines.GetCount(); ++i) + { + const SplineComponent& spline = scene->splines[i]; + if (spline.terrain_modifier_amount > 0) + { + generator->splines.push_back(spline); + } + } wi::jobsystem::Execute(generator->workload, [=](wi::jobsystem::JobArgs a) { wi::Timer timer; @@ -931,32 +947,57 @@ namespace wi::terrain // Do a parallel for loop over all the chunk's vertices and compute their properties: wi::jobsystem::context ctx; ctx.priority = wi::jobsystem::Priority::Low; - wi::jobsystem::Dispatch(ctx, vertexCount, chunk_width, [&](wi::jobsystem::JobArgs args) { + + // Preload height grid with padding, because neighbors will need to be accessed to determine slopes: + constexpr int chunk_width_padded = chunk_width + 1; + constexpr uint32_t vertexCount_padded = chunk_width_padded * chunk_width_padded; + float heights_padded[chunk_width_padded][chunk_width_padded]; + wi::jobsystem::Dispatch(ctx, vertexCount_padded, chunk_width_padded * 4, [&](wi::jobsystem::JobArgs args) { uint32_t index = args.jobIndex; - const float x = (float(index % chunk_width) - chunk_half_width) * chunk_scale; - const float z = (float(index / chunk_width) - chunk_half_width) * chunk_scale; - XMVECTOR corners[3]; - XMFLOAT2 corner_offsets[3] = { - XMFLOAT2(0, 0), - XMFLOAT2(1, 0), - XMFLOAT2(0, 1), - }; - for (int i = 0; i < arraysize(corners); ++i) + const XMUINT2 coord = XMUINT2(index % chunk_width_padded, index / chunk_width_padded); + const float x = (float(coord.x) - chunk_half_width) * chunk_scale; + const float z = (float(coord.y) - chunk_half_width) * chunk_scale; + + float height = 0; + const XMFLOAT2 world_pos = XMFLOAT2(chunk_data.position.x + x, chunk_data.position.z + z); + for (auto& modifier : modifiers) { - float height = 0; - const XMFLOAT2 world_pos = XMFLOAT2(chunk_data.position.x + x + corner_offsets[i].x, chunk_data.position.z + z + corner_offsets[i].y); - for (auto& modifier : modifiers) - { - modifier->Apply(world_pos, height); - } - if (i == 0) - { - chunk_data.heightmap_data[index] = uint16_t(height * 65535); - } - height = wi::math::Lerp(bottomLevel, topLevel, height); - corners[i] = XMVectorSet(world_pos.x, height, world_pos.y, 0); + modifier->Apply(world_pos, height); } - const float height = XMVectorGetY(corners[0]); + height = lerp(bottomLevel, topLevel, height); + XMVECTOR corner = XMVectorSet(world_pos.x, height, world_pos.y, 0); + + // Apply splines: + for (size_t j = 0; j < generator->splines.size(); ++j) + { + const SplineComponent& spline = generator->splines[j]; + if (spline.terrain_modifier_amount <= 0 || !spline.aabb.intersects(corner)) + continue; + XMVECTOR P = XMVectorSetY(corner, -1000); + XMVECTOR S = spline.TraceSplinePlane(P, XMVectorSet(0, 1, 0, 0), 4); + S = spline.ClosestPointOnSpline(S, 4); + float splineheight = XMVectorGetY(S); + P = XMVectorSetY(P, splineheight); + float splinedist = wi::math::Distance(P, S); + height = lerp(splineheight, height, smoothstep(0.0f, 1.0f, saturate(std::max(0.0f, splinedist - spline.width) * sqr(spline.terrain_modifier_amount)))); + corner = XMVectorSetY(corner, height); + } + + heights_padded[coord.x][coord.y] = height; + }); + wi::jobsystem::Wait(ctx); + + wi::jobsystem::Dispatch(ctx, vertexCount, chunk_width * 4, [&](wi::jobsystem::JobArgs args) { + const uint32_t index = args.jobIndex; + const XMUINT2 coord = XMUINT2(index % chunk_width, index / chunk_width); + const float x = (float(coord.x) - chunk_half_width) * chunk_scale; + const float z = (float(coord.y) - chunk_half_width) * chunk_scale; + const float height = heights_padded[coord.x][coord.y]; + const XMVECTOR corners[3] = { + XMVectorSet(chunk_data.position.x + x, height, chunk_data.position.z + z, 0), + XMVectorSet(chunk_data.position.x + x + 1, heights_padded[coord.x + 1][coord.y], chunk_data.position.z + z, 0), + XMVectorSet(chunk_data.position.x + x, heights_padded[coord.x][coord.y + 1], chunk_data.position.z + z + 1, 0), + }; const XMVECTOR T = XMVectorSubtract(corners[1], corners[2]); const XMVECTOR B = XMVectorSubtract(corners[0], corners[1]); const XMVECTOR N = XMVector3Normalize(XMVector3Cross(T, B)); @@ -1006,7 +1047,8 @@ namespace wi::terrain { grass.vertex_lengths[index] = 0; } - }); + chunk_data.heightmap_data[index] = uint16_t(inverse_lerp(bottomLevel, topLevel, height) * 65535); + }); wi::jobsystem::Wait(ctx); // wait until chunk's vertex buffer is fully generated object.SetCastShadow(slope_cast_shadow.load()); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index a2b6b2faf..4962ec865 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 = 738; + const int revision = 739; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);