Terrain partial invalidation for spline component (#1309)
This commit is contained in:
@@ -33,7 +33,7 @@ template <typename T>
|
||||
constexpr T sqr(T x) { return x * x; }
|
||||
|
||||
template <typename T>
|
||||
constexpr T pow4(T x) { return x * x * x * x; }
|
||||
constexpr T pow4(T x) { return sqr(sqr(x)); }
|
||||
|
||||
template <typename T>
|
||||
constexpr T clamp(T x, T a, T b)
|
||||
|
||||
+20
-19
@@ -16,29 +16,26 @@ namespace wi
|
||||
uint32_t count = 0;
|
||||
constexpr bool isLeaf() const { return count > 0; }
|
||||
};
|
||||
wi::vector<uint8_t> allocation;
|
||||
Node* nodes = nullptr;
|
||||
wi::vector<Node> nodes;
|
||||
wi::vector<uint32_t> leaf_indices;
|
||||
uint32_t node_count = 0;
|
||||
uint32_t* leaf_indices = nullptr;
|
||||
uint32_t leaf_count = 0;
|
||||
|
||||
constexpr bool IsValid() const { return nodes != nullptr; }
|
||||
constexpr bool IsValid() const { return node_count > 0; }
|
||||
|
||||
// Completely rebuilds tree from scratch
|
||||
void Build(const wi::primitive::AABB* aabbs, uint32_t aabb_count)
|
||||
{
|
||||
node_count = 0;
|
||||
if (aabb_count == 0)
|
||||
return;
|
||||
|
||||
const uint32_t node_capacity = aabb_count * 2 - 1;
|
||||
allocation.reserve(
|
||||
sizeof(Node) * node_capacity +
|
||||
sizeof(uint32_t) * aabb_count
|
||||
);
|
||||
nodes = (Node*)allocation.data();
|
||||
leaf_indices = (uint32_t*)(nodes + node_capacity);
|
||||
leaf_count = aabb_count;
|
||||
if (aabb_count == 0)
|
||||
{
|
||||
nodes.clear();
|
||||
leaf_indices.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
nodes.resize(aabb_count * 2 - 1);
|
||||
leaf_indices.resize(aabb_count);
|
||||
|
||||
Node& node = nodes[node_count++];
|
||||
node = {};
|
||||
@@ -58,7 +55,7 @@ namespace wi
|
||||
return;
|
||||
if (aabb_count == 0)
|
||||
return;
|
||||
if (aabb_count != leaf_count)
|
||||
if (aabb_count != (uint32_t)leaf_indices.size())
|
||||
return;
|
||||
|
||||
for (uint32_t i = node_count - 1; i > 0; --i)
|
||||
@@ -88,7 +85,9 @@ namespace wi
|
||||
const std::function<void(uint32_t index)>& callback
|
||||
) const
|
||||
{
|
||||
Node& node = nodes[nodeIndex];
|
||||
if (node_count == 0)
|
||||
return;
|
||||
const Node& node = nodes[nodeIndex];
|
||||
if (!node.aabb.intersects(primitive))
|
||||
return;
|
||||
if (node.isLeaf())
|
||||
@@ -112,13 +111,15 @@ namespace wi
|
||||
const std::function<bool(uint32_t index)>& callback
|
||||
) const
|
||||
{
|
||||
if (node_count == 0)
|
||||
return false;
|
||||
uint32_t stack[64];
|
||||
uint32_t count = 0;
|
||||
stack[count++] = 0; // push node 0
|
||||
while (count > 0)
|
||||
while (count > 0 && (count < (arraysize(stack) - 1)))
|
||||
{
|
||||
const uint32_t nodeIndex = stack[--count];
|
||||
Node& node = nodes[nodeIndex];
|
||||
const Node& node = nodes[nodeIndex];
|
||||
if (!node.aabb.intersects(primitive))
|
||||
continue;
|
||||
if (node.isLeaf())
|
||||
|
||||
@@ -195,6 +195,16 @@ namespace wi::primitive
|
||||
bool intersection = frustum.Intersects(bb);
|
||||
return intersection;
|
||||
}
|
||||
bool AABB::intersects(const BoundingBox& other) const
|
||||
{
|
||||
BoundingBox bb = BoundingBox(getCenter(), getHalfWidth());
|
||||
return bb.Intersects(other);
|
||||
}
|
||||
bool AABB::intersects(const BoundingOrientedBox& other) const
|
||||
{
|
||||
BoundingBox bb = BoundingBox(getCenter(), getHalfWidth());
|
||||
return bb.Intersects(other);
|
||||
}
|
||||
AABB AABB::operator* (float a)
|
||||
{
|
||||
XMFLOAT3 min = getMin();
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace wi::primitive
|
||||
bool intersects(const Ray& ray) const;
|
||||
bool intersects(const Sphere& sphere) const;
|
||||
bool intersects(const BoundingFrustum& frustum) const;
|
||||
bool intersects(const BoundingBox& other) const;
|
||||
bool intersects(const BoundingOrientedBox& other) const;
|
||||
AABB operator* (float a);
|
||||
static AABB Merge(const AABB& a, const AABB& b);
|
||||
void AddPoint(const XMFLOAT3& pos);
|
||||
|
||||
@@ -18865,6 +18865,10 @@ void DrawBox(const XMFLOAT4X4& boxMatrix, const XMFLOAT4& color, bool depth)
|
||||
else
|
||||
renderableBoxes.push_back(std::make_pair(boxMatrix,color));
|
||||
}
|
||||
void DrawBox(const BoundingOrientedBox& obb, const XMFLOAT4& color, bool depth)
|
||||
{
|
||||
DrawBox(XMMatrixScalingFromVector(XMLoadFloat3(&obb.Extents)) * XMMatrixRotationQuaternion(XMLoadFloat4(&obb.Orientation)) * XMMatrixTranslationFromVector(XMLoadFloat3(&obb.Center)), color, depth);
|
||||
}
|
||||
void DrawSphere(const Sphere& sphere, const XMFLOAT4& color, bool depth)
|
||||
{
|
||||
if(depth)
|
||||
|
||||
@@ -1233,7 +1233,8 @@ namespace wi::renderer
|
||||
// Add box to render in next frame. It will be rendered in DrawDebugWorld()
|
||||
void DrawBox(const wi::primitive::AABB& aabb, const XMFLOAT4& color = XMFLOAT4(1, 1, 1, 1), bool depth = true);
|
||||
void DrawBox(const XMMATRIX& boxMatrix, const XMFLOAT4& color = XMFLOAT4(1, 1, 1, 1), bool depth = true);
|
||||
void DrawBox(const XMFLOAT4X4& boxMatrix, const XMFLOAT4& color = XMFLOAT4(1,1,1,1), bool depth = true);
|
||||
void DrawBox(const XMFLOAT4X4& boxMatrix, const XMFLOAT4& color = XMFLOAT4(1, 1, 1, 1), bool depth = true);
|
||||
void DrawBox(const BoundingOrientedBox& obb, const XMFLOAT4& color = XMFLOAT4(1,1,1,1), bool depth = true);
|
||||
// Add sphere to render in next frame. It will be rendered in DrawDebugWorld()
|
||||
void DrawSphere(const wi::primitive::Sphere& sphere, const XMFLOAT4& color = XMFLOAT4(1, 1, 1, 1), bool depth = true);
|
||||
// Add capsule to render in next frame. It will be rendered in DrawDebugWorld()
|
||||
|
||||
@@ -5943,6 +5943,8 @@ namespace wi::scene
|
||||
}
|
||||
void Scene::RunSplineUpdateSystem(wi::jobsystem::context& ctx)
|
||||
{
|
||||
ScopedCPUProfiling("Spline Update");
|
||||
|
||||
// On the main thread, check if any of them require mesh component, etc:
|
||||
for (size_t i = 0; i < splines.GetCount(); ++i)
|
||||
{
|
||||
@@ -6229,8 +6231,9 @@ namespace wi::scene
|
||||
// Compute AABB:
|
||||
if (dirty || (spline.dirty_terrain && spline.terrain_modifier_amount > 0))
|
||||
{
|
||||
spline.aabb = spline.ComputeAABB();
|
||||
spline.PrecomputeSplineBounds(4);
|
||||
}
|
||||
|
||||
});
|
||||
wi::jobsystem::Wait(ctx);
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ namespace wi::scene
|
||||
// The contents of the other scene will be lost (and moved to this)!
|
||||
// Any references to entities or components from the other scene will now reference them in this scene.
|
||||
virtual void Merge(Scene& other);
|
||||
// Similar to merge but skipping some things that are safe to skip within the Update look
|
||||
// Similar to merge but skipping some things that are safe to skip within the Update loop
|
||||
void MergeFastInternal(Scene& other);
|
||||
// Create a copy of prefab and merge it into this.
|
||||
// prefab : source scene to be copied from
|
||||
|
||||
@@ -2010,7 +2010,8 @@ namespace wi::scene
|
||||
size_t MeshComponent::GetMemoryUsageBVH() const
|
||||
{
|
||||
return
|
||||
bvh.allocation.capacity() +
|
||||
bvh.nodes.size() * sizeof(BVH::Node) +
|
||||
bvh.leaf_indices.size() * sizeof(uint32_t) +
|
||||
bvh_leaf_aabbs.size() * sizeof(wi::primitive::AABB);
|
||||
}
|
||||
size_t MeshComponent::GetClusterCount() const
|
||||
@@ -3227,4 +3228,69 @@ namespace wi::scene
|
||||
precomputed_node_distances[i] = distance;
|
||||
}
|
||||
}
|
||||
void SplineComponent::PrecomputeSplineBounds(int subdivision)
|
||||
{
|
||||
if (spline_node_entities.size() < 2)
|
||||
{
|
||||
precomputed_obbs.clear();
|
||||
precomputed_aabbs.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
float rangemod = width;
|
||||
if (terrain_modifier_amount > 0)
|
||||
{
|
||||
rangemod /= sqr(terrain_modifier_amount); // sqr is used to match with distance falloff used in terrain generation
|
||||
}
|
||||
const size_t count = (spline_node_entities.size() - 1) * subdivision;
|
||||
precomputed_obbs.resize(count);
|
||||
precomputed_aabbs.resize(count);
|
||||
const XMVECTOR XAXIS = XMVectorSet(1, 0, 0, 0);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
const float t0 = float(i) / count;
|
||||
const float t1 = float(i + 1) / count;
|
||||
const XMMATRIX M0 = EvaluateSplineAt(t0);
|
||||
const XMMATRIX M1 = EvaluateSplineAt(t1);
|
||||
const XMVECTOR P0 = wi::math::GetPosition(M0);
|
||||
const XMVECTOR P1 = wi::math::GetPosition(M1);
|
||||
const XMVECTOR C = XMVectorLerp(P0, P1, 0.5f);
|
||||
const XMVECTOR T = XMVector3Normalize(P1 - P0);
|
||||
const XMVECTOR A = XMVector3Normalize(XMVector3Cross(XAXIS, T));
|
||||
const float angle = XMScalarACos(XMVectorGetX(XMVector3Dot(XAXIS, T)));
|
||||
const XMVECTOR Q = XMQuaternionNormalize(XMQuaternionRotationNormal(A, angle));
|
||||
BoundingOrientedBox& obb = precomputed_obbs[i];
|
||||
XMStoreFloat3(&obb.Center, C);
|
||||
obb.Extents = XMFLOAT3(wi::math::Distance(P0, P1) * 0.5f + rangemod, rangemod, rangemod);
|
||||
XMStoreFloat4(&obb.Orientation, Q);
|
||||
|
||||
#if 0
|
||||
// DEBUG OBB:
|
||||
static wi::SpinLock locker;
|
||||
locker.lock();
|
||||
wi::renderer::DrawBox(precomputed_obbs[i]);
|
||||
locker.unlock();
|
||||
#endif
|
||||
|
||||
XMFLOAT3 corners[BoundingOrientedBox::CORNER_COUNT];
|
||||
obb.GetCorners(corners);
|
||||
AABB& aabb = precomputed_aabbs[i];
|
||||
aabb = {};
|
||||
for (auto& x : corners)
|
||||
{
|
||||
aabb.AddPoint(x);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// DEBUG AABB:
|
||||
static wi::SpinLock locker;
|
||||
locker.lock();
|
||||
wi::renderer::DrawBox(precomputed_aabbs[i]);
|
||||
locker.unlock();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bvh.Build(precomputed_aabbs.data(), (uint32_t)precomputed_aabbs.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2647,9 +2647,11 @@ namespace wi::scene
|
||||
mutable int prev_terrain_generation_nodes = 0;
|
||||
mutable bool dirty_terrain = false;
|
||||
bool prev_looped = false;
|
||||
wi::primitive::AABB aabb;
|
||||
wi::ecs::Entity materialEntity = wi::ecs::INVALID_ENTITY; // temp for terrain usage
|
||||
mutable wi::ecs::Entity materialEntity_terrainPrev = wi::ecs::INVALID_ENTITY; // temp for terrain usage
|
||||
wi::vector<BoundingOrientedBox> precomputed_obbs; // an array of OBBs that approximate the spline's volume
|
||||
wi::vector<wi::primitive::AABB> precomputed_aabbs; // an array of AABBs that approximate the spline's volume
|
||||
wi::BVH bvh; // BVH fitted onto the precomputed_aabbs for accelerated intersection checking. The leaf nodes can be used to index aabbs and obbs alike
|
||||
|
||||
// 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
|
||||
@@ -2661,12 +2663,17 @@ namespace wi::scene
|
||||
// Trace a point on the spline's plane:
|
||||
XMVECTOR TraceSplinePlane(const XMVECTOR& ORIGIN, const XMVECTOR& DIRECTION, int steps = 10) const;
|
||||
|
||||
// Compute the boounding box of the spline iteratively
|
||||
// Compute the bounding box of the spline iteratively
|
||||
wi::primitive::AABB ComputeAABB(int steps = 10) const;
|
||||
|
||||
// Precompute the spline node distances that will be used at spline evaluation calls
|
||||
void PrecomputeSplineNodeDistances();
|
||||
|
||||
// Compute the oriented and axis aligned bounding boxes of the spline iteratively that approximates the spline volume
|
||||
// Will write into the precomputed_aabbs and precomputed_obbs array, subdivision mean how many boxes will be used per-segment
|
||||
// Will also build the bvh
|
||||
void PrecomputeSplineBounds(int subdivision = 10);
|
||||
|
||||
// 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; }
|
||||
void SetDrawAligned(bool value = true) { if (value) { _flags |= DRAW_ALIGNED; } else { _flags &= ~DRAW_ALIGNED; } }
|
||||
|
||||
+86
-12
@@ -13,6 +13,7 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
|
||||
using namespace wi::ecs;
|
||||
using namespace wi::scene;
|
||||
@@ -183,6 +184,8 @@ namespace wi::terrain
|
||||
wi::jobsystem::context workload;
|
||||
std::atomic_bool cancelled{ false };
|
||||
wi::vector<SplineComponent> splines;
|
||||
wi::vector<Chunk> removable_chunks; // chunks that were invalidated are regenerated on the generator thread. Before merging them with the scene, the previous version of them will need to be removed from the destination scene
|
||||
std::deque<Chunk> priority_invalidation; // to not let invalidation stuck at same chunks every frame while editing splines, for more appealing visual feedback
|
||||
};
|
||||
|
||||
wi::jobsystem::context virtual_texture_ctx;
|
||||
@@ -605,23 +608,40 @@ namespace wi::terrain
|
||||
const SplineComponent& spline = scene->splines[i];
|
||||
if (spline.terrain_modifier_amount > 0 || spline.prev_terrain_modifier_amount > 0)
|
||||
{
|
||||
restart_generation |= spline.dirty_terrain;
|
||||
if (restart_generation)
|
||||
bool spline_terrain_invalidation = false;
|
||||
if (spline.dirty_terrain)
|
||||
{
|
||||
spline.dirty_terrain = false;
|
||||
spline_terrain_invalidation = true;
|
||||
spline.prev_terrain_modifier_amount = spline.terrain_modifier_amount;
|
||||
spline.prev_terrain_pushdown = spline.terrain_pushdown;
|
||||
spline.prev_terrain_texture_falloff = spline.terrain_texture_falloff;
|
||||
spline.prev_terrain_generation_nodes = (int)spline.spline_node_entities.size();
|
||||
}
|
||||
restart_generation |= spline.materialEntity != spline.materialEntity_terrainPrev;
|
||||
spline_terrain_invalidation |= spline.materialEntity != spline.materialEntity_terrainPrev;
|
||||
const MaterialComponent* splineMaterial = scene->materials.GetComponent(spline.materialEntity);
|
||||
if (splineMaterial != nullptr)
|
||||
{
|
||||
restart_generation |= splineMaterial->IsDirty();
|
||||
spline_terrain_invalidation |= splineMaterial->IsDirty();
|
||||
splineMaterialEntities.push_back(spline.materialEntity);
|
||||
}
|
||||
spline.materialEntity_terrainPrev = spline.materialEntity;
|
||||
|
||||
if (spline_terrain_invalidation)
|
||||
{
|
||||
for (auto it = chunks.begin(); it != chunks.end(); it++)
|
||||
{
|
||||
ChunkData& chunk_data = it->second;
|
||||
if (chunk_data.invalidated)
|
||||
continue;
|
||||
BoundingBox bb(chunk_data.sphere.center, XMFLOAT3(chunk_data.sphere.radius, 1000000, chunk_data.sphere.radius));
|
||||
if (spline.bvh.IntersectsFirst(bb, [&](uint32_t index) { return spline.precomputed_obbs[index].Intersects(bb); }))
|
||||
{
|
||||
chunk_data.invalidated = true;
|
||||
generator->priority_invalidation.push_back(it->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,6 +676,23 @@ namespace wi::terrain
|
||||
weather = *weather_component; // feedback default weather
|
||||
}
|
||||
|
||||
// Invalidated chunks replacements, originals are removed before merging updated ones:
|
||||
for (Chunk chunk : generator->removable_chunks)
|
||||
{
|
||||
auto it = chunks.find(chunk);
|
||||
if (it != chunks.end())
|
||||
{
|
||||
ChunkData& chunk_data = it->second;
|
||||
scene->Entity_Remove(chunk_data.entity);
|
||||
chunk_data.props_entity = INVALID_ENTITY;
|
||||
if (chunk_data.vt != nullptr)
|
||||
{
|
||||
chunk_data.vt->invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
generator->removable_chunks.clear();
|
||||
|
||||
// What was generated, will be merged in to the main scene
|
||||
scene->MergeFastInternal(generator->scene);
|
||||
|
||||
@@ -900,9 +937,6 @@ namespace wi::terrain
|
||||
if (spline.terrain_modifier_amount > 0)
|
||||
{
|
||||
generator->splines.push_back(spline);
|
||||
// extrude spline aabb for heightfield check:
|
||||
generator->splines.back().aabb._min.y = -FLT_MAX;
|
||||
generator->splines.back().aabb._max.y = FLT_MAX;
|
||||
}
|
||||
}
|
||||
wi::jobsystem::Execute(generator->workload, [=](wi::jobsystem::JobArgs a) {
|
||||
@@ -916,12 +950,25 @@ namespace wi::terrain
|
||||
chunk.x += offset_x;
|
||||
chunk.z += offset_z;
|
||||
auto it = chunks.find(chunk);
|
||||
if (it == chunks.end() || it->second.entity == INVALID_ENTITY)
|
||||
if (it == chunks.end() || it->second.entity == INVALID_ENTITY || it->second.invalidated)
|
||||
{
|
||||
// Generate a new chunk:
|
||||
ChunkData& chunk_data = chunks[chunk];
|
||||
|
||||
chunk_data.entity = generator->scene.Entity_CreateObject("chunk_" + std::to_string(chunk.x) + "_" + std::to_string(chunk.z));
|
||||
std::string chunk_name = "chunk_" + std::to_string(chunk.x) + "_" + std::to_string(chunk.z);
|
||||
if (chunk_data.entity == INVALID_ENTITY)
|
||||
{
|
||||
chunk_data.entity = generator->scene.Entity_CreateObject(chunk_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// replacement will be made instead of simple merge, entity ID can be reused:
|
||||
generator->scene.names.Create(chunk_data.entity) = std::move(chunk_name);
|
||||
generator->scene.layers.Create(chunk_data.entity);
|
||||
generator->scene.transforms.Create(chunk_data.entity);
|
||||
generator->scene.objects.Create(chunk_data.entity);
|
||||
generator->removable_chunks.push_back(chunk);
|
||||
}
|
||||
ObjectComponent& object = *generator->scene.objects.GetComponent(chunk_data.entity);
|
||||
object.lod_bias = lod_bias;
|
||||
object.filterMask |= wi::enums::FILTER_NAVIGATION_MESH;
|
||||
@@ -1012,13 +1059,14 @@ namespace wi::terrain
|
||||
|
||||
// Apply splines to height only:
|
||||
const XMVECTOR P = XMVectorSet(world_pos.x, -100000, world_pos.y, 0);
|
||||
const wi::primitive::Ray ray(P, UP);
|
||||
int splinematerialcnt = -1;
|
||||
for (size_t j = 0; j < generator->splines.size(); ++j)
|
||||
{
|
||||
const SplineComponent& spline = generator->splines[j];
|
||||
if (spline.materialEntity != INVALID_ENTITY)
|
||||
splinematerialcnt++;
|
||||
if (!spline.aabb.intersects(P))
|
||||
if (!spline.bvh.IntersectsFirst(ray, [&](uint32_t index) { return spline.precomputed_aabbs[index].intersects(ray); }))
|
||||
continue;
|
||||
XMVECTOR S = spline.TraceSplinePlane(P, UP, 4);
|
||||
S = spline.ClosestPointOnSpline(S, 4);
|
||||
@@ -1134,6 +1182,8 @@ namespace wi::terrain
|
||||
}
|
||||
|
||||
// Create the textures for virtual texture update:
|
||||
chunk_data.heightmap = {};
|
||||
chunk_data.blendmap = {};
|
||||
CreateChunkRegionTexture(chunk_data);
|
||||
|
||||
if (IsPhysicsEnabled())
|
||||
@@ -1162,10 +1212,13 @@ namespace wi::terrain
|
||||
if (it != chunks.end() && it->second.entity != INVALID_ENTITY)
|
||||
{
|
||||
ChunkData& chunk_data = it->second;
|
||||
if (chunk_data.grass_entity == INVALID_ENTITY && chunk_data.grass.meshID != INVALID_ENTITY)
|
||||
if ((chunk_data.grass_entity == INVALID_ENTITY || chunk_data.invalidated) && chunk_data.grass.meshID != INVALID_ENTITY)
|
||||
{
|
||||
// add patch for this chunk
|
||||
chunk_data.grass_entity = CreateEntity();
|
||||
if (chunk_data.grass_entity == INVALID_ENTITY)
|
||||
{
|
||||
chunk_data.grass_entity = CreateEntity();
|
||||
}
|
||||
wi::HairParticleSystem& grass = generator->scene.hairs.Create(chunk_data.grass_entity);
|
||||
grass = chunk_data.grass;
|
||||
chunk_data.grass_density_current = grass_density;
|
||||
@@ -1297,6 +1350,13 @@ namespace wi::terrain
|
||||
}
|
||||
}
|
||||
|
||||
it = chunks.find(chunk); // re-query!
|
||||
if (it != chunks.end() && it->second.entity != INVALID_ENTITY)
|
||||
{
|
||||
ChunkData& chunk_data = it->second;
|
||||
chunk_data.invalidated = false;
|
||||
}
|
||||
|
||||
if (generated_something && timer.elapsed_milliseconds() > generation_time_budget_milliseconds)
|
||||
{
|
||||
generator->cancelled.store(true);
|
||||
@@ -1304,6 +1364,20 @@ namespace wi::terrain
|
||||
|
||||
};
|
||||
|
||||
// priority invalidation queue:
|
||||
// This doesn't necessarily finish every frame, that's why it's a queue, next frame will pick up earlier requests before newer ones
|
||||
while (!generator->priority_invalidation.empty())
|
||||
{
|
||||
Chunk chunk = generator->priority_invalidation.front();
|
||||
generator->priority_invalidation.pop_front();
|
||||
auto it = chunks.find(chunk);
|
||||
if (it != chunks.end() && it->second.invalidated) // Check here too in this special case, because multiple of the same entries can easily exist on the queue. Already refreshes chunks will not be refreshed again
|
||||
{
|
||||
request_chunk(chunk.x, chunk.z);
|
||||
if (generator->cancelled.load()) return;
|
||||
}
|
||||
}
|
||||
|
||||
// generate center chunk first:
|
||||
request_chunk(0, 0);
|
||||
if (generator->cancelled.load()) return;
|
||||
|
||||
@@ -230,6 +230,7 @@ namespace wi::terrain
|
||||
wi::primitive::Sphere sphere;
|
||||
XMFLOAT3 position = XMFLOAT3(0, 0, 0);
|
||||
bool visible = true;
|
||||
bool invalidated = false;
|
||||
std::shared_ptr<VirtualTexture> vt;
|
||||
wi::vector<uint16_t> heightmap_data;
|
||||
wi::graphics::Texture heightmap;
|
||||
|
||||
@@ -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 = 855;
|
||||
const int revision = 856;
|
||||
|
||||
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user