Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -51,74 +51,14 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
static void DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw);
|
||||
static void DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw);
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
ViewportIconsRendererService ViewportIconsRendererServiceInstance;
|
||||
|
||||
namespace
|
||||
{
|
||||
void DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.Frustum;
|
||||
auto& icons = scene->GetSceneRendering()->ViewportIcons;
|
||||
Matrix m1, m2, world;
|
||||
for (Actor* icon : icons)
|
||||
{
|
||||
BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS);
|
||||
IconTypes iconType;
|
||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType))
|
||||
{
|
||||
// Create world matrix
|
||||
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
|
||||
Matrix::RotationY(PI, world);
|
||||
Matrix::Multiply(m2, world, m1);
|
||||
Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2);
|
||||
Matrix::Multiply(m1, m2, world);
|
||||
|
||||
// Draw icon
|
||||
GeometryDrawStateData drawState;
|
||||
draw.DrawState = &drawState;
|
||||
draw.Buffer = &InstanceBuffers[static_cast<int32>(iconType)];
|
||||
draw.World = &world;
|
||||
draw.Bounds = sphere;
|
||||
QuadModel->Draw(renderContext, draw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.Frustum;
|
||||
Matrix m1, m2, world;
|
||||
BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS);
|
||||
IconTypes iconType;
|
||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
|
||||
{
|
||||
// Create world matrix
|
||||
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
|
||||
Matrix::RotationY(PI, world);
|
||||
Matrix::Multiply(m2, world, m1);
|
||||
Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2);
|
||||
Matrix::Multiply(m1, m2, world);
|
||||
|
||||
// Draw icon
|
||||
GeometryDrawStateData drawState;
|
||||
draw.DrawState = &drawState;
|
||||
draw.Buffer = &InstanceBuffers[static_cast<int32>(iconType)];
|
||||
draw.World = &world;
|
||||
draw.Bounds = sphere;
|
||||
QuadModel->Draw(renderContext, draw);
|
||||
}
|
||||
|
||||
for (auto child : actor->Children)
|
||||
DrawIcons(renderContext, child, draw);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
@@ -137,14 +77,73 @@ void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor
|
||||
|
||||
if (const auto scene = SceneObject::Cast<Scene>(actor))
|
||||
{
|
||||
::DrawIcons(renderContext, scene, draw);
|
||||
ViewportIconsRendererService::DrawIcons(renderContext, scene, draw);
|
||||
}
|
||||
else
|
||||
{
|
||||
::DrawIcons(renderContext, actor, draw);
|
||||
ViewportIconsRendererService::DrawIcons(renderContext, actor, draw);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene* scene, Mesh::DrawInfo& draw)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.Frustum;
|
||||
auto& icons = scene->GetSceneRendering()->ViewportIcons;
|
||||
Matrix m1, m2, world;
|
||||
for (Actor* icon : icons)
|
||||
{
|
||||
BoundingSphere sphere(icon->GetPosition(), ICON_RADIUS);
|
||||
IconTypes iconType;
|
||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(icon->GetTypeHandle(), iconType))
|
||||
{
|
||||
// Create world matrix
|
||||
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
|
||||
Matrix::RotationY(PI, world);
|
||||
Matrix::Multiply(m2, world, m1);
|
||||
Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2);
|
||||
Matrix::Multiply(m1, m2, world);
|
||||
|
||||
// Draw icon
|
||||
GeometryDrawStateData drawState;
|
||||
draw.DrawState = &drawState;
|
||||
draw.Buffer = &InstanceBuffers[static_cast<int32>(iconType)];
|
||||
draw.World = &world;
|
||||
draw.Bounds = sphere;
|
||||
QuadModel->Draw(renderContext, draw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor* actor, Mesh::DrawInfo& draw)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.Frustum;
|
||||
Matrix m1, m2, world;
|
||||
BoundingSphere sphere(actor->GetPosition(), ICON_RADIUS);
|
||||
IconTypes iconType;
|
||||
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
|
||||
{
|
||||
// Create world matrix
|
||||
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
|
||||
Matrix::RotationY(PI, world);
|
||||
Matrix::Multiply(m2, world, m1);
|
||||
Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2);
|
||||
Matrix::Multiply(m1, m2, world);
|
||||
|
||||
// Draw icon
|
||||
GeometryDrawStateData drawState;
|
||||
draw.DrawState = &drawState;
|
||||
draw.Buffer = &InstanceBuffers[static_cast<int32>(iconType)];
|
||||
draw.World = &world;
|
||||
draw.Bounds = sphere;
|
||||
QuadModel->Draw(renderContext, draw);
|
||||
}
|
||||
|
||||
for (auto child : actor->Children)
|
||||
DrawIcons(renderContext, child, draw);
|
||||
}
|
||||
|
||||
bool ViewportIconsRendererService::Init()
|
||||
{
|
||||
QuadModel = Content::LoadAsyncInternal<Model>(TEXT("Engine/Models/Quad"));
|
||||
|
||||
@@ -115,16 +115,6 @@ void MaterialInstance::OnBaseParamsChanged()
|
||||
ParamsChanged();
|
||||
}
|
||||
|
||||
void MaterialInstance::OnUnload()
|
||||
{
|
||||
if (_baseMaterial)
|
||||
{
|
||||
OnBaseUnset();
|
||||
_baseMaterial = nullptr;
|
||||
}
|
||||
Params.Dispose();
|
||||
}
|
||||
|
||||
bool MaterialInstance::IsMaterialInstance() const
|
||||
{
|
||||
return true;
|
||||
@@ -242,7 +232,12 @@ Asset::LoadResult MaterialInstance::load()
|
||||
|
||||
void MaterialInstance::unload(bool isReloading)
|
||||
{
|
||||
OnUnload();
|
||||
if (_baseMaterial)
|
||||
{
|
||||
OnBaseUnset();
|
||||
_baseMaterial = nullptr;
|
||||
}
|
||||
Params.Dispose();
|
||||
}
|
||||
|
||||
AssetChunksFlag MaterialInstance::getChunksToPreload() const
|
||||
|
||||
@@ -49,7 +49,6 @@ private:
|
||||
void OnBaseUnset();
|
||||
void OnBaseUnloaded(Asset* p);
|
||||
void OnBaseParamsChanged();
|
||||
void OnUnload();
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -466,6 +466,17 @@ public:
|
||||
_count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified item to the collection.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to add.</param>
|
||||
void Add(T&& item)
|
||||
{
|
||||
EnsureCapacity(_count + 1);
|
||||
Memory::MoveItems(_allocation.Get() + _count, &item, 1);
|
||||
_count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified item to the collection.
|
||||
/// </summary>
|
||||
|
||||
@@ -20,22 +20,15 @@ private:
|
||||
// TODO: don't use Array but small struct and don't InlinedArray or Chunk* but Chunk (less dynamic allocations)
|
||||
typedef Array<T> Chunk;
|
||||
|
||||
int32 _count;
|
||||
int32 _count = 0;
|
||||
Array<Chunk*, InlinedAllocation<32>> _chunks;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
ChunkedArray()
|
||||
: _count(0)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructor
|
||||
/// </summary>
|
||||
~ChunkedArray()
|
||||
{
|
||||
_chunks.ClearDelete();
|
||||
@@ -426,13 +419,21 @@ public:
|
||||
/// <param name="newSize">The new size.</param>
|
||||
void Resize(int32 newSize)
|
||||
{
|
||||
if (newSize < Count())
|
||||
while (newSize < Count())
|
||||
{
|
||||
MISSING_CODE("shrinking ChunkedArray on Resize");
|
||||
auto& chunk = _chunks.Last();
|
||||
int32 itemsLeft = Count() - newSize;
|
||||
int32 itemsToRemove = Math::Min(chunk->Count(), itemsLeft);
|
||||
chunk->Resize(chunk->Count() - itemsToRemove);
|
||||
_count -= itemsToRemove;
|
||||
if (chunk->Count() == 0)
|
||||
{
|
||||
Delete(chunk);
|
||||
_chunks.RemoveLast();
|
||||
}
|
||||
}
|
||||
else
|
||||
if (newSize > Count())
|
||||
{
|
||||
// Require enough space at once
|
||||
EnsureCapacity(newSize);
|
||||
|
||||
// Add more items until reach the new size
|
||||
@@ -445,9 +446,9 @@ public:
|
||||
|
||||
// Insert some items to this chunk if can
|
||||
const int32 spaceLeft = chunk->Capacity() - chunk->Count();
|
||||
int32 itemsToInsert = Math::Min(itemsReaming, spaceLeft);
|
||||
chunk->Resize(chunk->Count() + itemsToInsert);
|
||||
_count += itemsToInsert;
|
||||
int32 itemsToAdd = Math::Min(itemsReaming, spaceLeft);
|
||||
chunk->Resize(chunk->Count() + itemsToAdd);
|
||||
_count += itemsToAdd;
|
||||
|
||||
// Update counter
|
||||
itemsReaming = newSize - Count();
|
||||
@@ -481,29 +482,16 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets iterator for beginning of the collection.
|
||||
/// </summary>
|
||||
/// <returns>Iterator for beginning of the collection.</returns>
|
||||
Iterator Begin() const
|
||||
{
|
||||
return Iterator(this, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets iterator for ending of the collection.
|
||||
/// </summary>
|
||||
/// <returns>Iterator for ending of the collection.</returns>
|
||||
Iterator End() const
|
||||
{
|
||||
return Iterator(this, Count());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets iterator for the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <returns>Iterator for the specified index.</returns>
|
||||
Iterator IteratorAt(int32 index) const
|
||||
{
|
||||
return Iterator(this, index);
|
||||
|
||||
@@ -425,225 +425,6 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const bool value)
|
||||
{
|
||||
SetType(CommonType::Bool);
|
||||
AsBool = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const int32 value)
|
||||
{
|
||||
SetType(CommonType::Integer);
|
||||
AsInteger = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const float value)
|
||||
{
|
||||
SetType(CommonType::Float);
|
||||
AsFloat = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Vector2& value)
|
||||
{
|
||||
SetType(CommonType::Vector2);
|
||||
AsVector2 = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Vector3& value)
|
||||
{
|
||||
SetType(CommonType::Vector3);
|
||||
AsVector3 = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Vector4& value)
|
||||
{
|
||||
SetType(CommonType::Vector4);
|
||||
AsVector4 = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Color& value)
|
||||
{
|
||||
SetType(CommonType::Color);
|
||||
AsColor = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Matrix& value)
|
||||
{
|
||||
SetType(CommonType::Matrix);
|
||||
AsMatrix = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Guid& value)
|
||||
{
|
||||
SetType(CommonType::Guid);
|
||||
AsGuid = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const String& value)
|
||||
{
|
||||
Set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const StringView& value)
|
||||
{
|
||||
Set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const BoundingBox& value)
|
||||
{
|
||||
SetType(CommonType::Box);
|
||||
AsBox = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Quaternion& value)
|
||||
{
|
||||
SetType(CommonType::Rotation);
|
||||
AsRotation = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Transform& value)
|
||||
{
|
||||
SetType(CommonType::Transform);
|
||||
AsTransform = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const BoundingSphere& value)
|
||||
{
|
||||
SetType(CommonType::Sphere);
|
||||
AsSphere = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Rectangle& value)
|
||||
{
|
||||
SetType(CommonType::Rectangle);
|
||||
AsRectangle = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Ray& value)
|
||||
{
|
||||
SetType(CommonType::Ray);
|
||||
AsRay = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(const Char* value)
|
||||
{
|
||||
Set(StringView(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(void* value)
|
||||
{
|
||||
SetType(CommonType::Pointer);
|
||||
AsPointer = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator
|
||||
/// </summary>
|
||||
/// <param name="value">Value</param>
|
||||
CommonValue& operator=(ScriptingObject* value)
|
||||
{
|
||||
SetType(CommonType::Object);
|
||||
AsObject = value;
|
||||
if (value)
|
||||
LinkObject();
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
|
||||
LayersMask operator&(const LayersMask& other) const
|
||||
{
|
||||
return Mask && other.Mask;
|
||||
return Mask & other.Mask;
|
||||
}
|
||||
|
||||
LayersMask operator|(const LayersMask& other) const
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Forward declarations
|
||||
class Foliage;
|
||||
class FoliageCluster;
|
||||
class FoliageType;
|
||||
@@ -11,6 +10,12 @@ struct FoliageInstance;
|
||||
// Enable/disable foliage editing and changing at runtime. If your game need to use procedural foliage then enable this option.
|
||||
#define FOLIAGE_EDITING (USE_EDITOR)
|
||||
|
||||
// Enables using single quad-tree acceleration structure per foliage actor, otherwise will use quad-tree per foliage type to optimize drawing performance at a cost of higher memory usage.
|
||||
#define FOLIAGE_USE_SINGLE_QUAD_TREE 0
|
||||
|
||||
// Enables using manual draw calls batching instead of using automated generic solution in RenderList. Boosts performance for large foliage.
|
||||
#define FOLIAGE_USE_DRAW_CALLS_BATCHING 1
|
||||
|
||||
// Size of the instance allocation chunks (number of instances per allocated page)
|
||||
#define FOLIAGE_INSTANCE_CHUNKS_SIZE (4096*4)
|
||||
|
||||
|
||||
+433
-162
@@ -7,7 +7,11 @@
|
||||
#include "Engine/Core/Random.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#endif
|
||||
#include "Engine/Level/SceneQuery.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Serialization/Serialization.h"
|
||||
@@ -17,60 +21,9 @@ Foliage::Foliage(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
{
|
||||
_disableFoliageTypeEvents = false;
|
||||
Root = nullptr;
|
||||
}
|
||||
|
||||
void Foliage::EnsureRoot()
|
||||
{
|
||||
// Skip if root is already here or there is no instances at all
|
||||
if (Root || Instances.IsEmpty())
|
||||
return;
|
||||
ASSERT(Clusters.IsEmpty());
|
||||
|
||||
PROFILE_CPU();
|
||||
|
||||
// Calculate total bounds of valid instances
|
||||
BoundingBox totalBounds;
|
||||
{
|
||||
bool anyValid = false;
|
||||
// TODO: inline code and use SIMD
|
||||
BoundingBox box;
|
||||
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (!FoliageTypes[i->Type].IsReady())
|
||||
continue;
|
||||
|
||||
BoundingBox::FromSphere(i->Bounds, box);
|
||||
ASSERT(!i->Bounds.Center.IsNanOrInfinity());
|
||||
|
||||
if (anyValid)
|
||||
{
|
||||
BoundingBox::Merge(totalBounds, box, totalBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalBounds = box;
|
||||
anyValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip if nothing is valid
|
||||
if (!anyValid)
|
||||
return;
|
||||
}
|
||||
ASSERT(!totalBounds.Minimum.IsNanOrInfinity() && !totalBounds.Maximum.IsNanOrInfinity());
|
||||
|
||||
// Setup first and topmost cluster
|
||||
Clusters.Resize(1);
|
||||
Root = &Clusters[0];
|
||||
Root->Init(totalBounds);
|
||||
|
||||
// Cache bounds
|
||||
_box = Root->Bounds;
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
}
|
||||
|
||||
void Foliage::AddToCluster(FoliageCluster* cluster, FoliageInstance& instance)
|
||||
void Foliage::AddToCluster(ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_SIZE>& clusters, FoliageCluster* cluster, FoliageInstance& instance)
|
||||
{
|
||||
ASSERT(instance.Bounds.Radius > ZeroTolerance);
|
||||
ASSERT(cluster->Bounds.Intersects(instance.Bounds));
|
||||
@@ -100,12 +53,12 @@ void Foliage::AddToCluster(FoliageCluster* cluster, FoliageInstance& instance)
|
||||
else
|
||||
{
|
||||
// Subdivide cluster
|
||||
const int32 count = Clusters.Count();
|
||||
Clusters.Resize(count + 4);
|
||||
cluster->Children[0] = &Clusters[count + 0];
|
||||
cluster->Children[1] = &Clusters[count + 1];
|
||||
cluster->Children[2] = &Clusters[count + 2];
|
||||
cluster->Children[3] = &Clusters[count + 3];
|
||||
const int32 count = clusters.Count();
|
||||
clusters.Resize(count + 4);
|
||||
cluster->Children[0] = &clusters[count + 0];
|
||||
cluster->Children[1] = &clusters[count + 1];
|
||||
cluster->Children[2] = &clusters[count + 2];
|
||||
cluster->Children[3] = &clusters[count + 3];
|
||||
|
||||
// Setup children
|
||||
const Vector3 min = cluster->Bounds.Minimum;
|
||||
@@ -119,13 +72,239 @@ void Foliage::AddToCluster(FoliageCluster* cluster, FoliageInstance& instance)
|
||||
// Move instances to a proper cells
|
||||
for (int32 i = 0; i < cluster->Instances.Count(); i++)
|
||||
{
|
||||
AddToCluster(cluster, *cluster->Instances[i]);
|
||||
AddToCluster(clusters, cluster, *cluster->Instances[i]);
|
||||
}
|
||||
cluster->Instances.Clear();
|
||||
AddToCluster(cluster, instance);
|
||||
AddToCluster(clusters, cluster, instance);
|
||||
}
|
||||
}
|
||||
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
|
||||
void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, const ModelLOD& modelLod, float lodDitherFactor)
|
||||
{
|
||||
for (const auto& mesh : modelLod.Meshes)
|
||||
{
|
||||
const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()];
|
||||
if (!entry.Visible || !mesh.IsInitialized())
|
||||
return;
|
||||
const MaterialSlot& slot = model->MaterialSlots[mesh.GetMaterialSlotIndex()];
|
||||
const auto shadowsMode = static_cast<ShadowsCastingMode>(entry.ShadowsMode & slot.ShadowsMode);
|
||||
const auto drawModes = static_cast<DrawPass>(type._drawModes & renderContext.View.GetShadowsDrawPassMask(shadowsMode));
|
||||
|
||||
// Select material
|
||||
MaterialBase* material;
|
||||
if (entry.Material && entry.Material->IsLoaded())
|
||||
material = entry.Material;
|
||||
else if (slot.Material && slot.Material->IsLoaded())
|
||||
material = slot.Material;
|
||||
else
|
||||
material = GPUDevice::Instance->GetDefaultMaterial();
|
||||
if (!material || !material->IsSurface() || drawModes == DrawPass::None)
|
||||
return;
|
||||
|
||||
// Submit draw call
|
||||
DrawCall drawCall;
|
||||
mesh.GetDrawCallGeometry(drawCall);
|
||||
drawCall.InstanceCount = 1;
|
||||
drawCall.Material = material;
|
||||
drawCall.World = instance.World;
|
||||
drawCall.ObjectPosition = drawCall.World.GetTranslation();
|
||||
drawCall.Surface.GeometrySize = mesh.GetBox().GetSize();
|
||||
drawCall.Surface.PrevWorld = instance.World;
|
||||
drawCall.Surface.Lightmap = _staticFlags & StaticFlags::Lightmap ? _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex) : nullptr;
|
||||
drawCall.Surface.LightmapUVsArea = instance.Lightmap.UVsArea;
|
||||
drawCall.Surface.Skinning = nullptr;
|
||||
drawCall.Surface.LODDitherFactor = lodDitherFactor;
|
||||
drawCall.WorldDeterminantSign = 1;
|
||||
drawCall.PerInstanceRandom = instance.Random;
|
||||
renderContext.List->AddDrawCall(drawModes, _staticFlags, drawCall, entry.ReceiveDecals);
|
||||
}
|
||||
}
|
||||
|
||||
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type)
|
||||
{
|
||||
// Skip clusters that around too far from view
|
||||
if (Vector3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center) - cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
return;
|
||||
|
||||
//DebugDraw::DrawBox(cluster->Bounds, Color::Red);
|
||||
|
||||
// Draw visible children
|
||||
if (cluster->Children[0])
|
||||
{
|
||||
// Don't store instances in non-leaf nodes
|
||||
ASSERT_LOW_LAYER(cluster->Instances.IsEmpty());
|
||||
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
if (renderContext.View.CullingFrustum.Intersects(cluster->Children[idx]->TotalBounds)) \
|
||||
DrawCluster(renderContext, cluster->Children[idx], type)
|
||||
DRAW_CLUSTER(0);
|
||||
DRAW_CLUSTER(1);
|
||||
DRAW_CLUSTER(2);
|
||||
DRAW_CLUSTER(3);
|
||||
#undef DRAW_CLUSTER
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw visible instances
|
||||
const auto frame = Engine::FrameCount;
|
||||
const auto model = type.Model.Get();
|
||||
for (int32 i = 0; i < cluster->Instances.Count(); i++)
|
||||
{
|
||||
auto& instance = *cluster->Instances[i];
|
||||
if (Vector3::Distance(renderContext.View.Position, instance.Bounds.Center) - instance.Bounds.Radius < instance.CullDistance &&
|
||||
renderContext.View.CullingFrustum.Intersects(instance.Bounds))
|
||||
{
|
||||
const auto modelFrame = instance.DrawState.PrevFrame + 1;
|
||||
|
||||
// Select a proper LOD index (model may be culled)
|
||||
int32 lodIndex = RenderTools::ComputeModelLOD(model, instance.Bounds.Center, instance.Bounds.Radius, renderContext);
|
||||
if (lodIndex == -1)
|
||||
{
|
||||
// Handling model fade-out transition
|
||||
if (modelFrame == frame && instance.DrawState.PrevLOD != -1)
|
||||
{
|
||||
// Check if start transition
|
||||
if (instance.DrawState.LODTransition == 255)
|
||||
{
|
||||
instance.DrawState.LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(instance.DrawState.LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (instance.DrawState.LODTransition == 255)
|
||||
{
|
||||
instance.DrawState.PrevLOD = lodIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[prevLOD], normalizedProgress);
|
||||
}
|
||||
}
|
||||
instance.DrawState.PrevFrame = frame;
|
||||
continue;
|
||||
}
|
||||
lodIndex += renderContext.View.ModelLODBias;
|
||||
lodIndex = model->ClampLODIndex(lodIndex);
|
||||
|
||||
// Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports)
|
||||
if (modelFrame == frame)
|
||||
{
|
||||
// Check if start transition
|
||||
if (instance.DrawState.PrevLOD != lodIndex && instance.DrawState.LODTransition == 255)
|
||||
{
|
||||
instance.DrawState.LODTransition = 0;
|
||||
}
|
||||
|
||||
RenderTools::UpdateModelLODTransition(instance.DrawState.LODTransition);
|
||||
|
||||
// Check if end transition
|
||||
if (instance.DrawState.LODTransition == 255)
|
||||
{
|
||||
instance.DrawState.PrevLOD = lodIndex;
|
||||
}
|
||||
}
|
||||
// Check if there was a gap between frames in drawing this model instance
|
||||
else if (modelFrame < frame || instance.DrawState.PrevLOD == -1)
|
||||
{
|
||||
// Reset state
|
||||
instance.DrawState.PrevLOD = lodIndex;
|
||||
instance.DrawState.LODTransition = 255;
|
||||
}
|
||||
|
||||
// Draw
|
||||
if (instance.DrawState.PrevLOD == lodIndex)
|
||||
{
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], 0.0f);
|
||||
}
|
||||
else if (instance.DrawState.PrevLOD == -1)
|
||||
{
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], 1.0f - normalizedProgress);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto prevLOD = model->ClampLODIndex(instance.DrawState.PrevLOD);
|
||||
const float normalizedProgress = static_cast<float>(instance.DrawState.LODTransition) * (1.0f / 255.0f);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[prevLOD], normalizedProgress);
|
||||
DrawInstance(renderContext, instance, type, model, model->LODs[lodIndex], normalizedProgress - 1.0f);
|
||||
}
|
||||
|
||||
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen);
|
||||
|
||||
instance.DrawState.PrevFrame = frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw)
|
||||
{
|
||||
// Skip clusters that around too far from view
|
||||
if (Vector3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center) - cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
return;
|
||||
|
||||
//DebugDraw::DrawBox(cluster->Bounds, Color::Red);
|
||||
|
||||
// Draw visible children
|
||||
if (cluster->Children[0])
|
||||
{
|
||||
// Don't store instances in non-leaf nodes
|
||||
ASSERT_LOW_LAYER(cluster->Instances.IsEmpty());
|
||||
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
if (renderContext.View.CullingFrustum.Intersects(cluster->Children[idx]->TotalBounds)) \
|
||||
DrawCluster(renderContext, cluster->Children[idx], draw)
|
||||
DRAW_CLUSTER(0);
|
||||
DRAW_CLUSTER(1);
|
||||
DRAW_CLUSTER(2);
|
||||
DRAW_CLUSTER(3);
|
||||
#undef DRAW_CLUSTER
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw visible instances
|
||||
const auto frame = Engine::FrameCount;
|
||||
for (int32 i = 0; i < cluster->Instances.Count(); i++)
|
||||
{
|
||||
auto& instance = *cluster->Instances[i];
|
||||
auto& type = FoliageTypes[instance.Type];
|
||||
|
||||
// Check if can draw this instance
|
||||
if (type._canDraw &&
|
||||
Vector3::Distance(renderContext.View.Position, instance.Bounds.Center) - instance.Bounds.Radius < instance.CullDistance &&
|
||||
renderContext.View.CullingFrustum.Intersects(instance.Bounds))
|
||||
{
|
||||
// Disable motion blur
|
||||
instance.DrawState.PrevWorld = instance.World;
|
||||
|
||||
// Draw model
|
||||
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex);
|
||||
draw.LightmapUVs = &instance.Lightmap.UVsArea;
|
||||
draw.Buffer = &type.Entries;
|
||||
draw.World = &instance.World;
|
||||
draw.DrawState = &instance.DrawState;
|
||||
draw.Bounds = instance.Bounds;
|
||||
draw.PerInstanceRandom = instance.Random;
|
||||
draw.DrawModes = type._drawModes;
|
||||
type.Model->Draw(renderContext, draw);
|
||||
|
||||
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen);
|
||||
|
||||
instance.DrawState.PrevFrame = frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int32 Foliage::GetInstancesCount() const
|
||||
{
|
||||
return Instances.Count();
|
||||
@@ -363,16 +542,121 @@ void Foliage::RebuildClusters()
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
// Remove previous clusters data
|
||||
Root = nullptr;
|
||||
Clusters.Clear();
|
||||
// Faster path if foliage is empty or no types is ready
|
||||
bool anyTypeValid = false;
|
||||
for (auto& type : FoliageTypes)
|
||||
anyTypeValid |= type.IsReady();
|
||||
if (!anyTypeValid || Instances.IsEmpty())
|
||||
{
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
Root = nullptr;
|
||||
Clusters.Clear();
|
||||
#else
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
type.Root = nullptr;
|
||||
type.Clusters.Clear();
|
||||
}
|
||||
#endif
|
||||
_box = BoundingBox(_transform.Translation, _transform.Translation);
|
||||
_sphere = BoundingSphere(_transform.Translation, 0.0f);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureRoot();
|
||||
// Clear clusters and initialize root
|
||||
{
|
||||
PROFILE_CPU_NAMED("Init Root");
|
||||
|
||||
BoundingBox totalBounds, box;
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
{
|
||||
// Calculate total bounds of all instances
|
||||
auto i = Instances.Begin();
|
||||
for (; i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (!FoliageTypes[i->Type].IsReady())
|
||||
continue;
|
||||
BoundingBox::FromSphere(i->Bounds, box);
|
||||
totalBounds = box;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
// TODO: inline code and use SIMD
|
||||
for (; i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (!FoliageTypes[i->Type].IsReady())
|
||||
continue;
|
||||
BoundingBox::FromSphere(i->Bounds, box);
|
||||
BoundingBox::Merge(totalBounds, box, totalBounds);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup first and topmost cluster
|
||||
Clusters.Resize(1);
|
||||
Root = &Clusters[0];
|
||||
Root->Init(totalBounds);
|
||||
#else
|
||||
bool hasTotalBounds = false;
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
if (!type.IsReady())
|
||||
{
|
||||
type.Root = nullptr;
|
||||
type.Clusters.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Calculate total bounds of all instances of this type
|
||||
BoundingBox totalBoundsType;
|
||||
auto i = Instances.Begin();
|
||||
for (; i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Type == type.Index)
|
||||
{
|
||||
BoundingBox::FromSphere(i->Bounds, box);
|
||||
totalBoundsType = box;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++i;
|
||||
// TODO: inline code and use SIMD
|
||||
for (; i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (i->Type == type.Index)
|
||||
{
|
||||
BoundingBox::FromSphere(i->Bounds, box);
|
||||
BoundingBox::Merge(totalBoundsType, box, totalBoundsType);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup first and topmost cluster
|
||||
type.Clusters.Resize(1);
|
||||
type.Root = &type.Clusters[0];
|
||||
type.Root->Init(totalBoundsType);
|
||||
if (hasTotalBounds)
|
||||
{
|
||||
BoundingBox::Merge(totalBounds, totalBoundsType, totalBounds);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalBounds = totalBoundsType;
|
||||
hasTotalBounds = true;
|
||||
}
|
||||
}
|
||||
ASSERT(hasTotalBounds);
|
||||
#endif
|
||||
ASSERT(!totalBounds.Minimum.IsNanOrInfinity() && !totalBounds.Maximum.IsNanOrInfinity());
|
||||
_box = totalBounds;
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
// Insert all instances to the clusters
|
||||
{
|
||||
PROFILE_CPU_NAMED("Create Clusters");
|
||||
|
||||
const float globalDensityScale = GetGlobalDensityScale();
|
||||
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
@@ -382,17 +666,31 @@ void Foliage::RebuildClusters()
|
||||
|
||||
if (type.IsReady() && instance.Random < densityScale)
|
||||
{
|
||||
AddToCluster(Root, instance);
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
AddToCluster(Clusters, Root, instance);
|
||||
#else
|
||||
AddToCluster(type.Clusters, type.Root, instance);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
if (Root)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Update Cache");
|
||||
|
||||
Root->UpdateTotalBoundsAndCullDistance();
|
||||
}
|
||||
#else
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
if (type.Root)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Update Cache");
|
||||
type.Root->UpdateTotalBoundsAndCullDistance();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Foliage::UpdateCullDistance()
|
||||
@@ -401,7 +699,6 @@ void Foliage::UpdateCullDistance()
|
||||
|
||||
{
|
||||
PROFILE_CPU_NAMED("Instances");
|
||||
|
||||
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
auto& instance = *i;
|
||||
@@ -410,12 +707,22 @@ void Foliage::UpdateCullDistance()
|
||||
}
|
||||
}
|
||||
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
if (Root)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Clusters");
|
||||
|
||||
Root->UpdateCullDistance();
|
||||
}
|
||||
#else
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
if (type.Root)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Clusters");
|
||||
type.Root->UpdateCullDistance();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static float GlobalDensityScale = 1.0f;
|
||||
@@ -454,117 +761,62 @@ bool Foliage::Intersects(const Ray& ray, float& distance, Vector3& normal, int32
|
||||
PROFILE_CPU();
|
||||
|
||||
instanceIndex = -1;
|
||||
|
||||
if (Root)
|
||||
{
|
||||
FoliageInstance* instance;
|
||||
if (Root->Intersects(this, ray, distance, normal, instance))
|
||||
{
|
||||
int32 j = 0;
|
||||
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (&*i == instance)
|
||||
{
|
||||
instanceIndex = j;
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
distance = MAX_float;
|
||||
normal = Vector3::Up;
|
||||
return false;
|
||||
}
|
||||
distance = MAX_float;
|
||||
|
||||
void Foliage::DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw)
|
||||
{
|
||||
// Skip clusters that around too far from view
|
||||
if (Vector3::Distance(renderContext.View.Position, cluster->TotalBoundsSphere.Center) - cluster->TotalBoundsSphere.Radius > cluster->MaxCullDistance)
|
||||
return;
|
||||
|
||||
//DebugDraw::DrawBox(cluster->Bounds, Color::Red);
|
||||
|
||||
// Draw visible children
|
||||
if (cluster->Children[0])
|
||||
FoliageInstance* instance = nullptr;
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
if (Root)
|
||||
Root->Intersects(this, ray, distance, normal, instance);
|
||||
#else
|
||||
float tmpDistance;
|
||||
Vector3 tmpNormal;
|
||||
FoliageInstance* tmpInstance;
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
#if BUILD_DEBUG
|
||||
// Don't store instances in non-leaf nodes
|
||||
ASSERT(cluster->Instances.IsEmpty());
|
||||
#endif
|
||||
|
||||
#define DRAW_CLUSTER(idx) \
|
||||
if (renderContext.View.CullingFrustum.Intersects(cluster->Children[idx]->TotalBounds)) \
|
||||
DrawCluster(renderContext, cluster->Children[idx], draw)
|
||||
DRAW_CLUSTER(0);
|
||||
DRAW_CLUSTER(1);
|
||||
DRAW_CLUSTER(2);
|
||||
DRAW_CLUSTER(3);
|
||||
#undef DRAW_CLUSTER
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw visible instances
|
||||
const auto frame = Engine::FrameCount;
|
||||
for (int32 i = 0; i < cluster->Instances.Count(); i++)
|
||||
if (type.Root && type.Root->Intersects(this, ray, tmpDistance, tmpNormal, tmpInstance) && tmpDistance < distance)
|
||||
{
|
||||
auto& instance = *cluster->Instances[i];
|
||||
auto& type = FoliageTypes[instance.Type];
|
||||
|
||||
// Check if can draw this instance
|
||||
if (type._canDraw &&
|
||||
Vector3::Distance(renderContext.View.Position, instance.Bounds.Center) - instance.Bounds.Radius < instance.CullDistance &&
|
||||
renderContext.View.CullingFrustum.Intersects(instance.Bounds))
|
||||
{
|
||||
// Disable motion blur
|
||||
instance.DrawState.PrevWorld = instance.World;
|
||||
|
||||
// Draw model
|
||||
draw.Lightmap = GetScene()->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex);
|
||||
draw.LightmapUVs = &instance.Lightmap.UVsArea;
|
||||
draw.Buffer = &type.Entries;
|
||||
draw.World = &instance.World;
|
||||
draw.DrawState = &instance.DrawState;
|
||||
draw.Bounds = instance.Bounds;
|
||||
draw.PerInstanceRandom = instance.Random;
|
||||
draw.DrawModes = type._drawModes;
|
||||
type.Model->Draw(renderContext, draw);
|
||||
|
||||
//DebugDraw::DrawSphere(instance.Bounds, Color::YellowGreen);
|
||||
|
||||
instance.DrawState.PrevFrame = frame;
|
||||
}
|
||||
distance = tmpDistance;
|
||||
normal = tmpNormal;
|
||||
instance = tmpInstance;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (instance != nullptr)
|
||||
{
|
||||
int32 j = 0;
|
||||
for (auto i = Instances.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
if (&*i == instance)
|
||||
{
|
||||
instanceIndex = j;
|
||||
return true;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Foliage::Draw(RenderContext& renderContext)
|
||||
{
|
||||
// Skip if no instances spawned
|
||||
if (Instances.IsEmpty() || !Root)
|
||||
if (Instances.IsEmpty())
|
||||
return;
|
||||
auto& view = renderContext.View;
|
||||
|
||||
PROFILE_CPU();
|
||||
|
||||
// Cache data per foliage instance type
|
||||
for (int32 i = 0; i < FoliageTypes.Count(); i++)
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
auto& type = FoliageTypes[i];
|
||||
|
||||
const auto drawModes = static_cast<DrawPass>(type.DrawModes & view.Pass & (int32)view.GetShadowsDrawPassMask(type.ShadowsMode));
|
||||
type._canDraw = type.IsReady() && drawModes != DrawPass::None;
|
||||
type._drawModes = drawModes;
|
||||
|
||||
if (type._canDraw)
|
||||
{
|
||||
for (int32 j = 0; j < type.Entries.Count(); j++)
|
||||
{
|
||||
auto& e = type.Entries[j];
|
||||
|
||||
e.ReceiveDecals = type.ReceiveDecals != 0;
|
||||
e.ShadowsMode = type.ShadowsMode;
|
||||
}
|
||||
@@ -572,13 +824,24 @@ void Foliage::Draw(RenderContext& renderContext)
|
||||
}
|
||||
|
||||
// Draw visible clusters
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
Mesh::DrawInfo draw;
|
||||
draw.Flags = GetStaticFlags();
|
||||
draw.DrawModes = (DrawPass)(DrawPass::Default & view.Pass);
|
||||
draw.LODBias = 0;
|
||||
draw.ForcedLOD = -1;
|
||||
draw.VertexColors = nullptr;
|
||||
DrawCluster(renderContext, Root, draw);
|
||||
if (Root)
|
||||
DrawCluster(renderContext, Root, draw);
|
||||
#else
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
if (type.Root && type._canDraw && type.Model->CanBeRendered())
|
||||
{
|
||||
DrawCluster(renderContext, type.Root, type);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Foliage::DrawGeneric(RenderContext& renderContext)
|
||||
@@ -640,10 +903,10 @@ void Foliage::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
|
||||
stream.JKEY("Foliage");
|
||||
stream.StartArray();
|
||||
for (int32 i = 0; i < FoliageTypes.Count(); i++)
|
||||
for (auto& type : FoliageTypes)
|
||||
{
|
||||
stream.StartObject();
|
||||
FoliageTypes[i].Serialize(stream, nullptr);
|
||||
type.Serialize(stream, nullptr);
|
||||
stream.EndObject();
|
||||
}
|
||||
stream.EndArray();
|
||||
@@ -676,9 +939,11 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
|
||||
PROFILE_CPU();
|
||||
|
||||
// Clear
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
Root = nullptr;
|
||||
Instances.Release();
|
||||
Clusters.Release();
|
||||
#endif
|
||||
Instances.Release();
|
||||
FoliageTypes.Resize(0, false);
|
||||
|
||||
// Deserialize foliage types
|
||||
@@ -785,9 +1050,15 @@ void Foliage::Deserialize(DeserializeStream& stream, ISerializeModifier* modifie
|
||||
}
|
||||
}
|
||||
|
||||
void Foliage::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void Foliage::OnEnable()
|
||||
{
|
||||
GetScene()->Rendering.AddGeometry(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
@@ -795,7 +1066,7 @@ void Foliage::OnEnable()
|
||||
|
||||
void Foliage::OnDisable()
|
||||
{
|
||||
GetScene()->Rendering.RemoveGeometry(this);
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "FoliageCluster.h"
|
||||
#include "FoliageType.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Core/Collections/ChunkedArray.h"
|
||||
|
||||
/// <summary>
|
||||
/// Represents a foliage actor that contains a set of instanced meshes.
|
||||
@@ -19,23 +18,26 @@ DECLARE_SCENE_OBJECT(Foliage);
|
||||
private:
|
||||
|
||||
bool _disableFoliageTypeEvents;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The root cluster. Contains all the instances and it's the starting point of the quad-tree hierarchy. Null if no foliage added. It's read-only.
|
||||
/// </summary>
|
||||
FoliageCluster* Root;
|
||||
|
||||
/// <summary>
|
||||
/// The allocated foliage instances. It's read-only.
|
||||
/// </summary>
|
||||
ChunkedArray<FoliageInstance, FOLIAGE_INSTANCE_CHUNKS_SIZE> Instances;
|
||||
|
||||
#if FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
/// <summary>
|
||||
/// The root cluster. Contains all the instances and it's the starting point of the quad-tree hierarchy. Null if no foliage added. It's read-only.
|
||||
/// </summary>
|
||||
FoliageCluster* Root = nullptr;
|
||||
|
||||
/// <summary>
|
||||
/// The allocated foliage clusters. It's read-only.
|
||||
/// </summary>
|
||||
ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_SIZE> Clusters;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The foliage instances types used by the current foliage actor. It's read-only.
|
||||
@@ -140,36 +142,22 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices.
|
||||
/// </summary>
|
||||
/// <returns>The value.</returns>
|
||||
API_PROPERTY() static float GetGlobalDensityScale();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
API_PROPERTY() static void SetGlobalDensityScale(float value);
|
||||
|
||||
private:
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the root node of the foliage was added.
|
||||
/// </summary>
|
||||
void EnsureRoot();
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given foliage instance to the cluster.
|
||||
/// </summary>
|
||||
/// <param name="cluster">The root cluster.</param>
|
||||
/// <param name="instance">The instance.</param>
|
||||
void AddToCluster(FoliageCluster* cluster, FoliageInstance& instance);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the cluster.
|
||||
/// </summary>
|
||||
/// <param name="renderContext">The rendering context.</param>
|
||||
/// <param name="cluster">The cluster.</param>
|
||||
/// <param name="draw">The draw data.</param>
|
||||
void AddToCluster(ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_SIZE>& clusters, FoliageCluster* cluster, FoliageInstance& instance);
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE && FOLIAGE_USE_DRAW_CALLS_BATCHING
|
||||
void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, FoliageType& type, Model* model, const ModelLOD& modelLod, float lodDitherFactor);
|
||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, FoliageType& type);
|
||||
#else
|
||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, Mesh::DrawInfo& draw);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -191,6 +179,7 @@ public:
|
||||
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
void OnLayerChanged() override;
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ public:
|
||||
/// <summary>
|
||||
/// Determines whether this foliage instance has valid lightmap data.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if this foliage instance has valid lightmap data; otherwise, <c>false</c>.</returns>
|
||||
FORCE_INLINE bool HasLightmap() const
|
||||
{
|
||||
return Lightmap.TextureIndex != INVALID_INDEX;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
#include "Engine/Core/Collections/ChunkedArray.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Serialization/ISerializable.h"
|
||||
|
||||
@@ -86,6 +87,18 @@ public:
|
||||
/// The shared model instance entries.
|
||||
/// </summary>
|
||||
ModelInstanceEntries Entries;
|
||||
|
||||
#if !FOLIAGE_USE_SINGLE_QUAD_TREE
|
||||
/// <summary>
|
||||
/// The root cluster. Contains all the instances and it's the starting point of the quad-tree hierarchy. Null if no foliage added. It's read-only.
|
||||
/// </summary>
|
||||
FoliageCluster* Root = nullptr;
|
||||
|
||||
/// <summary>
|
||||
/// The allocated foliage clusters. It's read-only.
|
||||
/// </summary>
|
||||
ChunkedArray<FoliageCluster, FOLIAGE_CLUSTER_CHUNKS_SIZE> Clusters;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@@ -206,7 +219,6 @@ public:
|
||||
/// <summary>
|
||||
/// Determines whether this instance is ready (model is loaded).
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if foliage type model is loaded and instance type is ready; otherwise, <c>false</c>.</returns>
|
||||
FORCE_INLINE bool IsReady() const
|
||||
{
|
||||
return _isReady != 0;
|
||||
@@ -215,7 +227,6 @@ public:
|
||||
/// <summary>
|
||||
/// Gets the random scale for the foliage instance of this type.
|
||||
/// </summary>
|
||||
/// <returns>The scale vector.</returns>
|
||||
Vector3 GetRandomScale() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -439,6 +439,8 @@ void AnimatedModel::UpdateBounds()
|
||||
|
||||
BoundingBox::Transform(_boxLocal, _world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void AnimatedModel::UpdateSockets()
|
||||
@@ -758,4 +760,6 @@ void AnimatedModel::OnTransformChanged()
|
||||
_transform.GetWorld(_world);
|
||||
BoundingBox::Transform(_boxLocal, _world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,12 @@ void Decal::OnDebugDrawSelected()
|
||||
|
||||
#endif
|
||||
|
||||
void Decal::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void Decal::Draw(RenderContext& renderContext)
|
||||
{
|
||||
if ((renderContext.View.Flags & ViewFlags::Decals) != 0 &&
|
||||
@@ -109,7 +115,7 @@ bool Decal::IntersectsItself(const Ray& ray, float& distance, Vector3& normal)
|
||||
|
||||
void Decal::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddCommon(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddCommon(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -123,7 +129,7 @@ void Decal::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommon(this);
|
||||
GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
@@ -142,4 +148,7 @@ void Decal::OnTransformChanged()
|
||||
_bounds.Transformation = _world;
|
||||
_bounds.GetBoundingBox(_box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ private:
|
||||
Vector3 _size;
|
||||
OrientedBoundingBox _bounds;
|
||||
Matrix _world;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
void Draw(RenderContext& renderContext) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
|
||||
@@ -20,7 +20,8 @@ EnvironmentProbe::EnvironmentProbe(const SpawnParams& params)
|
||||
, _isUsingCustomProbe(false)
|
||||
, _probe(nullptr)
|
||||
{
|
||||
UpdateBounds();
|
||||
_sphere = BoundingSphere(Vector3::Zero, _radius);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
}
|
||||
|
||||
float EnvironmentProbe::GetRadius() const
|
||||
@@ -125,6 +126,8 @@ void EnvironmentProbe::UpdateBounds()
|
||||
{
|
||||
_sphere = BoundingSphere(GetPosition(), GetScaledRadius());
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void EnvironmentProbe::Draw(RenderContext& renderContext)
|
||||
@@ -150,6 +153,12 @@ void EnvironmentProbe::OnDebugDrawSelected()
|
||||
|
||||
#endif
|
||||
|
||||
void EnvironmentProbe::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
// Base
|
||||
@@ -190,7 +199,7 @@ bool EnvironmentProbe::IntersectsItself(const Ray& ray, float& distance, Vector3
|
||||
|
||||
void EnvironmentProbe::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddCommon(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddCommon(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -204,7 +213,7 @@ void EnvironmentProbe::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommon(this);
|
||||
GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -16,6 +16,7 @@ private:
|
||||
|
||||
float _radius;
|
||||
bool _isUsingCustomProbe;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
AssetReference<CubeTexture> _probe;
|
||||
|
||||
public:
|
||||
@@ -138,6 +139,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
bool HasContentLoaded() const override;
|
||||
|
||||
@@ -31,9 +31,15 @@ MaterialInstance* ModelInstanceActor::CreateAndSetVirtualMaterialInstance(int32
|
||||
return result;
|
||||
}
|
||||
|
||||
void ModelInstanceActor::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void ModelInstanceActor::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddGeometry(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
@@ -41,7 +47,7 @@ void ModelInstanceActor::OnEnable()
|
||||
|
||||
void ModelInstanceActor::OnDisable()
|
||||
{
|
||||
GetSceneRendering()->RemoveGeometry(this);
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
API_CLASS(Abstract) class FLAXENGINE_API ModelInstanceActor : public Actor
|
||||
{
|
||||
DECLARE_SCENE_OBJECT_ABSTRACT(ModelInstanceActor);
|
||||
protected:
|
||||
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
@@ -79,6 +83,11 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [Actor]
|
||||
void OnLayerChanged() override;
|
||||
|
||||
protected:
|
||||
|
||||
// [Actor]
|
||||
|
||||
@@ -15,7 +15,9 @@ PointLight::PointLight(const SpawnParams& params)
|
||||
ShadowsDistance = 2000.0f;
|
||||
ShadowsFadeDistance = 100.0f;
|
||||
ShadowsDepthBias = 0.5f;
|
||||
UpdateBounds();
|
||||
_direction = Vector3::Forward;
|
||||
_sphere = BoundingSphere(Vector3::Zero, _radius);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
}
|
||||
|
||||
float PointLight::ComputeBrightness() const
|
||||
@@ -62,11 +64,14 @@ void PointLight::UpdateBounds()
|
||||
// Cache bounding box
|
||||
_sphere = BoundingSphere(GetPosition(), GetScaledRadius());
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void PointLight::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddCommon(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddCommon(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -80,7 +85,7 @@ void PointLight::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommon(this);
|
||||
GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
LightWithShadow::OnDisable();
|
||||
@@ -157,6 +162,12 @@ void PointLight::OnDebugDrawSelected()
|
||||
|
||||
#endif
|
||||
|
||||
void PointLight::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void PointLight::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
// Base
|
||||
|
||||
@@ -16,6 +16,7 @@ private:
|
||||
|
||||
Vector3 _direction;
|
||||
float _radius;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
|
||||
@@ -101,6 +102,7 @@ public:
|
||||
void OnDebugDraw() override;
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
|
||||
|
||||
@@ -18,7 +18,14 @@ SpotLight::SpotLight(const SpawnParams& params)
|
||||
ShadowsDistance = 2000.0f;
|
||||
ShadowsFadeDistance = 100.0f;
|
||||
ShadowsDepthBias = 0.5f;
|
||||
UpdateBounds();
|
||||
|
||||
_direction = Vector3::Forward;
|
||||
_cosOuterCone = Math::Cos(_outerConeAngle * DegreesToRadians);
|
||||
_cosInnerCone = Math::Cos(_innerConeAngle * DegreesToRadians);
|
||||
_invCosConeDifference = 1.0f / (_cosInnerCone - _cosOuterCone);
|
||||
const float boundsRadius = Math::Sqrt(1.25f * _radius * _radius - _radius * _radius * _cosOuterCone);
|
||||
_sphere = BoundingSphere(GetPosition() + 0.5f * GetDirection() * _radius, boundsRadius);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
}
|
||||
|
||||
float SpotLight::ComputeBrightness() const
|
||||
@@ -105,11 +112,14 @@ void SpotLight::UpdateBounds()
|
||||
const float boundsRadius = Math::Sqrt(1.25f * radius * radius - radius * radius * _cosOuterCone);
|
||||
_sphere = BoundingSphere(GetPosition() + 0.5f * GetDirection() * radius, boundsRadius);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateCommon(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void SpotLight::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddCommon(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddCommon(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
#endif
|
||||
@@ -123,7 +133,7 @@ void SpotLight::OnDisable()
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveCommon(this);
|
||||
GetSceneRendering()->RemoveCommon(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
LightWithShadow::OnDisable();
|
||||
|
||||
@@ -21,6 +21,7 @@ private:
|
||||
float _cosOuterCone;
|
||||
float _cosInnerCone;
|
||||
float _invCosConeDifference;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -193,6 +193,8 @@ void StaticModel::UpdateBounds()
|
||||
_box = BoundingBox(_transform.Translation);
|
||||
}
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
bool StaticModel::HasContentLoaded() const
|
||||
@@ -245,7 +247,7 @@ void StaticModel::Draw(RenderContext& renderContext)
|
||||
draw.DrawState = &_drawState;
|
||||
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
|
||||
draw.LightmapUVs = &Lightmap.UVsArea;
|
||||
draw.Flags = GetStaticFlags();
|
||||
draw.Flags = _staticFlags;
|
||||
draw.DrawModes = drawModes;
|
||||
draw.Bounds = _sphere;
|
||||
draw.PerInstanceRandom = GetPerInstanceRandom();
|
||||
|
||||
@@ -588,9 +588,9 @@ public:
|
||||
{
|
||||
LOG(Info, "Prepare scene objects");
|
||||
SceneBeginData beginData;
|
||||
for (int32 i = 0; i < Level::Scenes.Count(); i++)
|
||||
for (auto scene : Level::Scenes)
|
||||
{
|
||||
Level::Scenes[i]->BeginPlay(&beginData);
|
||||
scene->BeginPlay(&beginData);
|
||||
}
|
||||
beginData.OnDone();
|
||||
}
|
||||
|
||||
@@ -30,13 +30,37 @@ bool SceneAsset::IsInternalType() const
|
||||
return true;
|
||||
}
|
||||
|
||||
BoundingBox SceneNavigation::GetNavigationBounds()
|
||||
{
|
||||
if (Volumes.IsEmpty())
|
||||
return BoundingBox::Empty;
|
||||
PROFILE_CPU_NAMED("GetNavigationBounds");
|
||||
auto box = Volumes[0]->GetBox();
|
||||
for (int32 i = 1; i < Volumes.Count(); i++)
|
||||
BoundingBox::Merge(box, Volumes[i]->GetBox(), box);
|
||||
return box;
|
||||
}
|
||||
|
||||
NavMeshBoundsVolume* SceneNavigation::FindNavigationBoundsOverlap(const BoundingBox& bounds)
|
||||
{
|
||||
NavMeshBoundsVolume* result = nullptr;
|
||||
for (int32 i = 0; i < Volumes.Count(); i++)
|
||||
{
|
||||
if (Volumes[i]->GetBox().Intersects(bounds))
|
||||
{
|
||||
result = Volumes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define CSG_COLLIDER_NAME TEXT("CSG.Collider")
|
||||
#define CSG_MODEL_NAME TEXT("CSG.Model")
|
||||
|
||||
Scene::Scene(const SpawnParams& params)
|
||||
: Actor(params)
|
||||
, Rendering(this)
|
||||
, Ticking(this)
|
||||
, LightmapsData(this)
|
||||
, CSGData(this)
|
||||
{
|
||||
@@ -65,31 +89,6 @@ void Scene::SetLightmapSettings(const LightmapSettings& value)
|
||||
Info.LightmapSettings = value;
|
||||
}
|
||||
|
||||
BoundingBox Scene::GetNavigationBounds()
|
||||
{
|
||||
if (NavigationVolumes.IsEmpty())
|
||||
return BoundingBox::Empty;
|
||||
PROFILE_CPU_NAMED("GetNavigationBounds");
|
||||
auto box = NavigationVolumes[0]->GetBox();
|
||||
for (int32 i = 1; i < NavigationVolumes.Count(); i++)
|
||||
BoundingBox::Merge(box, NavigationVolumes[i]->GetBox(), box);
|
||||
return box;
|
||||
}
|
||||
|
||||
NavMeshBoundsVolume* Scene::FindNavigationBoundsOverlap(const BoundingBox& bounds)
|
||||
{
|
||||
NavMeshBoundsVolume* result = nullptr;
|
||||
for (int32 i = 0; i < NavigationVolumes.Count(); i++)
|
||||
{
|
||||
if (NavigationVolumes[i]->GetBox().Intersects(bounds))
|
||||
{
|
||||
result = NavigationVolumes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Scene::ClearLightmaps()
|
||||
{
|
||||
LightmapsData.ClearLightmaps();
|
||||
@@ -283,7 +282,7 @@ void Scene::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
||||
CSGData.DeserializeIfExists(stream, "CSG", modifier);
|
||||
|
||||
// [Deprecated on 13.01.2021, expires on 13.01.2023]
|
||||
if (modifier->EngineBuild <= 6215 && NavigationMeshes.IsEmpty())
|
||||
if (modifier->EngineBuild <= 6215 && Navigation.Meshes.IsEmpty())
|
||||
{
|
||||
const auto e = SERIALIZE_FIND_MEMBER(stream, "NavMesh");
|
||||
if (e != stream.MemberEnd())
|
||||
|
||||
@@ -8,22 +8,18 @@
|
||||
#include "SceneCSGData.h"
|
||||
#include "SceneRendering.h"
|
||||
#include "SceneTicking.h"
|
||||
#include "SceneNavigation.h"
|
||||
|
||||
class MeshCollider;
|
||||
class Level;
|
||||
class ReloadScriptsAction;
|
||||
class NavMeshBoundsVolume;
|
||||
class NavMesh;
|
||||
|
||||
/// <summary>
|
||||
/// The scene root object that contains a hierarchy of actors.
|
||||
/// </summary>
|
||||
API_CLASS() class FLAXENGINE_API Scene : public Actor
|
||||
{
|
||||
friend class Level;
|
||||
friend class ReloadScriptsAction;
|
||||
DECLARE_SCENE_OBJECT(Scene);
|
||||
friend Level;
|
||||
friend ReloadScriptsAction;
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="Scene"/> class.
|
||||
@@ -47,8 +43,6 @@ public:
|
||||
/// </summary>
|
||||
DateTime SaveTime;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The scene rendering manager.
|
||||
/// </summary>
|
||||
@@ -59,6 +53,11 @@ public:
|
||||
/// </summary>
|
||||
SceneTicking Ticking;
|
||||
|
||||
/// <summary>
|
||||
/// The navigation data.
|
||||
/// </summary>
|
||||
SceneNavigation Navigation;
|
||||
|
||||
/// <summary>
|
||||
/// The static light manager for this scene.
|
||||
/// </summary>
|
||||
@@ -80,31 +79,6 @@ public:
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetLightmapSettings(const LightmapSettings& value);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The list of registered navigation bounds volumes (in the scene).
|
||||
/// </summary>
|
||||
Array<NavMeshBoundsVolume*> NavigationVolumes;
|
||||
|
||||
/// <summary>
|
||||
/// The list of registered navigation meshes (in the scene).
|
||||
/// </summary>
|
||||
Array<NavMesh*> NavigationMeshes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total navigation volumes bounds.
|
||||
/// </summary>
|
||||
/// <returns>The navmesh bounds.</returns>
|
||||
BoundingBox GetNavigationBounds();
|
||||
|
||||
/// <summary>
|
||||
/// Finds the navigation volume bounds that have intersection with the given world-space bounding box.
|
||||
/// </summary>
|
||||
/// <param name="bounds">The bounds.</param>
|
||||
/// <returns>The intersecting volume or null if none found.</returns>
|
||||
NavMeshBoundsVolume* FindNavigationBoundsOverlap(const BoundingBox& bounds);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
@@ -119,8 +93,6 @@ public:
|
||||
/// <param name="timeoutMs">The timeout to wait before building CSG (in milliseconds).</param>
|
||||
API_FUNCTION() void BuildCSG(float timeoutMs = 50);
|
||||
|
||||
public:
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Content/Assets/RawDataAsset.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
#include "Engine/Serialization/ISerializable.h"
|
||||
#include "Engine/Core/Math/Triangle.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Physics/CollisionData.h"
|
||||
#include "Engine/Serialization/ISerializable.h"
|
||||
#include "Engine/Content/AssetReference.h"
|
||||
#include "Engine/Content/Assets/RawDataAsset.h"
|
||||
#include "Engine/Content/Assets/Model.h"
|
||||
|
||||
class Scene;
|
||||
|
||||
@@ -74,7 +74,6 @@ namespace CSG
|
||||
/// <summary>
|
||||
/// Determines whether this container has CSG data linked.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c> if this CSG data container is valid; otherwise, <c>false</c>.</returns>
|
||||
bool HasData() const;
|
||||
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
|
||||
class NavMesh;
|
||||
class NavMeshBoundsVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Scene navigation subsystem.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SceneNavigation
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The list of registered navigation bounds volumes (on the scene).
|
||||
/// </summary>
|
||||
Array<NavMeshBoundsVolume*> Volumes;
|
||||
|
||||
/// <summary>
|
||||
/// The list of registered navigation meshes (on the scene).
|
||||
/// </summary>
|
||||
Array<NavMesh*> Meshes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total navigation volumes bounds.
|
||||
/// </summary>
|
||||
BoundingBox GetNavigationBounds();
|
||||
|
||||
/// <summary>
|
||||
/// Finds the navigation volume bounds that have intersection with the given world-space bounding box.
|
||||
/// </summary>
|
||||
/// <param name="bounds">The bounds.</param>
|
||||
/// <returns>The intersecting volume or null if none found.</returns>
|
||||
NavMeshBoundsVolume* FindNavigationBoundsOverlap(const BoundingBox& bounds);
|
||||
};
|
||||
@@ -4,242 +4,315 @@
|
||||
#include "Scene.h"
|
||||
#include "Engine/Graphics/RenderTask.h"
|
||||
#include "Engine/Graphics/RenderView.h"
|
||||
#include "Engine/Level/Actors/PostFxVolume.h"
|
||||
|
||||
#define SCENE_RENDERING_USE_PROFILER 0
|
||||
#define SCENE_RENDERING_USE_SIMD 0
|
||||
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#endif
|
||||
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
|
||||
#include "Engine/Core/SIMD.h"
|
||||
|
||||
ALIGN_BEGIN(16) struct CullDataSIMD
|
||||
{
|
||||
float xs[8];
|
||||
float ys[8];
|
||||
float zs[8];
|
||||
float ds[8];
|
||||
float xs[8];
|
||||
float ys[8];
|
||||
float zs[8];
|
||||
float ds[8];
|
||||
} ALIGN_END(16);
|
||||
|
||||
#endif
|
||||
|
||||
int32 SceneRendering::DrawEntries::Add(Actor* obj)
|
||||
{
|
||||
int32 key = 0;
|
||||
for (; key < List.Count(); key++)
|
||||
{
|
||||
if (List[key].Actor == nullptr)
|
||||
break;
|
||||
}
|
||||
if (key == List.Count())
|
||||
List.AddOne();
|
||||
auto& e = List[key];
|
||||
e.Actor = obj;
|
||||
e.LayerMask = obj->GetLayerMask();
|
||||
e.Bounds = obj->GetSphere();
|
||||
return key;
|
||||
}
|
||||
|
||||
void SceneRendering::DrawEntries::Update(Actor* obj, int32 key)
|
||||
{
|
||||
auto& e = List[key];
|
||||
ASSERT_LOW_LAYER(obj == e.Actor);
|
||||
e.LayerMask = obj->GetLayerMask();
|
||||
e.Bounds = obj->GetSphere();
|
||||
}
|
||||
|
||||
void SceneRendering::DrawEntries::Remove(Actor* obj, int32 key)
|
||||
{
|
||||
if (List.IsEmpty())
|
||||
return;
|
||||
auto& e = List[key];
|
||||
ASSERT_LOW_LAYER(obj == e.Actor);
|
||||
e.Actor = nullptr;
|
||||
e.LayerMask = 0;
|
||||
}
|
||||
|
||||
void SceneRendering::DrawEntries::Clear()
|
||||
{
|
||||
List.Clear();
|
||||
}
|
||||
|
||||
void SceneRendering::DrawEntries::CullAndDraw(RenderContext& renderContext)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.CullingFrustum;
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
CullDataSIMD cullData;
|
||||
{
|
||||
// Near
|
||||
auto plane = view.Frustum.GetNear();
|
||||
cullData.xs[0] = plane.Normal.X;
|
||||
cullData.ys[0] = plane.Normal.Y;
|
||||
cullData.zs[0] = plane.Normal.Z;
|
||||
cullData.ds[0] = plane.D;
|
||||
|
||||
// Far
|
||||
plane = view.Frustum.GetFar();
|
||||
cullData.xs[1] = plane.Normal.X;
|
||||
cullData.ys[1] = plane.Normal.Y;
|
||||
cullData.zs[1] = plane.Normal.Z;
|
||||
cullData.ds[1] = plane.D;
|
||||
|
||||
// Left
|
||||
plane = view.Frustum.GetLeft();
|
||||
cullData.xs[2] = plane.Normal.X;
|
||||
cullData.ys[2] = plane.Normal.Y;
|
||||
cullData.zs[2] = plane.Normal.Z;
|
||||
cullData.ds[2] = plane.D;
|
||||
|
||||
// Right
|
||||
plane = view.Frustum.GetRight();
|
||||
cullData.xs[3] = plane.Normal.X;
|
||||
cullData.ys[3] = plane.Normal.Y;
|
||||
cullData.zs[3] = plane.Normal.Z;
|
||||
cullData.ds[3] = plane.D;
|
||||
|
||||
// Top
|
||||
plane = view.Frustum.GetTop();
|
||||
cullData.xs[4] = plane.Normal.X;
|
||||
cullData.ys[4] = plane.Normal.Y;
|
||||
cullData.zs[4] = plane.Normal.Z;
|
||||
cullData.ds[4] = plane.D;
|
||||
|
||||
// Bottom
|
||||
plane = view.Frustum.GetBottom();
|
||||
cullData.xs[5] = plane.Normal.X;
|
||||
cullData.ys[5] = plane.Normal.Y;
|
||||
cullData.zs[5] = plane.Normal.Z;
|
||||
cullData.ds[5] = plane.D;
|
||||
|
||||
// Extra 0
|
||||
cullData.xs[6] = 0;
|
||||
cullData.ys[6] = 0;
|
||||
cullData.zs[6] = 0;
|
||||
cullData.ds[6] = 0;
|
||||
|
||||
// Extra 1
|
||||
cullData.xs[7] = 0;
|
||||
cullData.ys[7] = 0;
|
||||
cullData.zs[7] = 0;
|
||||
cullData.ds[7] = 0;
|
||||
}
|
||||
|
||||
SimdVector4 px = SIMD::Load(cullData.xs);
|
||||
SimdVector4 py = SIMD::Load(cullData.ys);
|
||||
SimdVector4 pz = SIMD::Load(cullData.zs);
|
||||
SimdVector4 pd = SIMD::Load(cullData.ds);
|
||||
SimdVector4 px2 = SIMD::Load(&cullData.xs[4]);
|
||||
SimdVector4 py2 = SIMD::Load(&cullData.ys[4]);
|
||||
SimdVector4 pz2 = SIMD::Load(&cullData.zs[4]);
|
||||
SimdVector4 pd2 = SIMD::Load(&cullData.ds[4]);
|
||||
|
||||
for (int32 i = 0; i < List.Count(); i++)
|
||||
{
|
||||
auto e = List[i];
|
||||
|
||||
SimdVector4 cx = SIMD::Splat(e.Bounds.Center.X);
|
||||
SimdVector4 cy = SIMD::Splat(e.Bounds.Center.Y);
|
||||
SimdVector4 cz = SIMD::Splat(e.Bounds.Center.Z);
|
||||
SimdVector4 r = SIMD::Splat(-e.Bounds.Radius);
|
||||
|
||||
SimdVector4 t = SIMD::Mul(cx, px);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz));
|
||||
t = SIMD::Add(t, pd);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
t = SIMD::Mul(cx, px2);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py2));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz2));
|
||||
t = SIMD::Add(t, pd2);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
if (view.RenderLayersMask.Mask & e.LayerMask)
|
||||
{
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
PROFILE_CPU();
|
||||
___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length());
|
||||
#endif
|
||||
e.Actor->Draw(renderContext);
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (int32 i = 0; i < List.Count(); i++)
|
||||
{
|
||||
auto e = List[i];
|
||||
if (view.RenderLayersMask.Mask & e.LayerMask && frustum.Intersects(e.Bounds))
|
||||
{
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
PROFILE_CPU();
|
||||
___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length());
|
||||
#endif
|
||||
e.Actor->Draw(renderContext);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneRendering::DrawEntries::CullAndDrawOffline(RenderContext& renderContext)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
const BoundingFrustum frustum = view.CullingFrustum;
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
CullDataSIMD cullData;
|
||||
{
|
||||
// Near
|
||||
auto plane = view.Frustum.GetNear();
|
||||
cullData.xs[0] = plane.Normal.X;
|
||||
cullData.ys[0] = plane.Normal.Y;
|
||||
cullData.zs[0] = plane.Normal.Z;
|
||||
cullData.ds[0] = plane.D;
|
||||
|
||||
// Far
|
||||
plane = view.Frustum.GetFar();
|
||||
cullData.xs[1] = plane.Normal.X;
|
||||
cullData.ys[1] = plane.Normal.Y;
|
||||
cullData.zs[1] = plane.Normal.Z;
|
||||
cullData.ds[1] = plane.D;
|
||||
|
||||
// Left
|
||||
plane = view.Frustum.GetLeft();
|
||||
cullData.xs[2] = plane.Normal.X;
|
||||
cullData.ys[2] = plane.Normal.Y;
|
||||
cullData.zs[2] = plane.Normal.Z;
|
||||
cullData.ds[2] = plane.D;
|
||||
|
||||
// Right
|
||||
plane = view.Frustum.GetRight();
|
||||
cullData.xs[3] = plane.Normal.X;
|
||||
cullData.ys[3] = plane.Normal.Y;
|
||||
cullData.zs[3] = plane.Normal.Z;
|
||||
cullData.ds[3] = plane.D;
|
||||
|
||||
// Top
|
||||
plane = view.Frustum.GetTop();
|
||||
cullData.xs[4] = plane.Normal.X;
|
||||
cullData.ys[4] = plane.Normal.Y;
|
||||
cullData.zs[4] = plane.Normal.Z;
|
||||
cullData.ds[4] = plane.D;
|
||||
|
||||
// Bottom
|
||||
plane = view.Frustum.GetBottom();
|
||||
cullData.xs[5] = plane.Normal.X;
|
||||
cullData.ys[5] = plane.Normal.Y;
|
||||
cullData.zs[5] = plane.Normal.Z;
|
||||
cullData.ds[5] = plane.D;
|
||||
|
||||
// Extra 0
|
||||
cullData.xs[6] = 0;
|
||||
cullData.ys[6] = 0;
|
||||
cullData.zs[6] = 0;
|
||||
cullData.ds[6] = 0;
|
||||
|
||||
// Extra 1
|
||||
cullData.xs[7] = 0;
|
||||
cullData.ys[7] = 0;
|
||||
cullData.zs[7] = 0;
|
||||
cullData.ds[7] = 0;
|
||||
}
|
||||
|
||||
SimdVector4 px = SIMD::Load(cullData.xs);
|
||||
SimdVector4 py = SIMD::Load(cullData.ys);
|
||||
SimdVector4 pz = SIMD::Load(cullData.zs);
|
||||
SimdVector4 pd = SIMD::Load(cullData.ds);
|
||||
SimdVector4 px2 = SIMD::Load(&cullData.xs[4]);
|
||||
SimdVector4 py2 = SIMD::Load(&cullData.ys[4]);
|
||||
SimdVector4 pz2 = SIMD::Load(&cullData.zs[4]);
|
||||
SimdVector4 pd2 = SIMD::Load(&cullData.ds[4]);
|
||||
|
||||
for (int32 i = 0; i < List.Count(); i++)
|
||||
{
|
||||
auto e = List[i];
|
||||
|
||||
SimdVector4 cx = SIMD::Splat(e.Bounds.Center.X);
|
||||
SimdVector4 cy = SIMD::Splat(e.Bounds.Center.Y);
|
||||
SimdVector4 cz = SIMD::Splat(e.Bounds.Center.Z);
|
||||
SimdVector4 r = SIMD::Splat(-e.Bounds.Radius);
|
||||
|
||||
SimdVector4 t = SIMD::Mul(cx, px);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz));
|
||||
t = SIMD::Add(t, pd);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
t = SIMD::Mul(cx, px2);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py2));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz2));
|
||||
t = SIMD::Add(t, pd2);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
if (view.RenderLayersMask.Mask & e.LayerMask && e.Actor->GetStaticFlags() & renderContext.View.StaticFlagsMask)
|
||||
{
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
PROFILE_CPU();
|
||||
___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length());
|
||||
#endif
|
||||
e.Actor->Draw(renderContext);
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (int32 i = 0; i < List.Count(); i++)
|
||||
{
|
||||
auto e = List[i];
|
||||
if (view.RenderLayersMask.Mask & e.LayerMask && frustum.Intersects(e.Bounds) && e.Actor->GetStaticFlags() & view.StaticFlagsMask)
|
||||
{
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
PROFILE_CPU();
|
||||
___tracy_scoped_zone.Name(*e.Actor->GetName(), e.Actor->GetName().Length());
|
||||
#endif
|
||||
e.Actor->Draw(renderContext);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SceneRendering::SceneRendering(::Scene* scene)
|
||||
: Scene(scene)
|
||||
{
|
||||
}
|
||||
|
||||
void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
CullDataSIMD cullData;
|
||||
{
|
||||
// Near
|
||||
auto plane = view.Frustum.GetNear();
|
||||
cullData.xs[0] = plane.Normal.X;
|
||||
cullData.ys[0] = plane.Normal.Y;
|
||||
cullData.zs[0] = plane.Normal.Z;
|
||||
cullData.ds[0] = plane.D;
|
||||
|
||||
// Far
|
||||
plane = view.Frustum.GetFar();
|
||||
cullData.xs[1] = plane.Normal.X;
|
||||
cullData.ys[1] = plane.Normal.Y;
|
||||
cullData.zs[1] = plane.Normal.Z;
|
||||
cullData.ds[1] = plane.D;
|
||||
|
||||
// Left
|
||||
plane = view.Frustum.GetLeft();
|
||||
cullData.xs[2] = plane.Normal.X;
|
||||
cullData.ys[2] = plane.Normal.Y;
|
||||
cullData.zs[2] = plane.Normal.Z;
|
||||
cullData.ds[2] = plane.D;
|
||||
|
||||
// Right
|
||||
plane = view.Frustum.GetRight();
|
||||
cullData.xs[3] = plane.Normal.X;
|
||||
cullData.ys[3] = plane.Normal.Y;
|
||||
cullData.zs[3] = plane.Normal.Z;
|
||||
cullData.ds[3] = plane.D;
|
||||
|
||||
// Top
|
||||
plane = view.Frustum.GetTop();
|
||||
cullData.xs[4] = plane.Normal.X;
|
||||
cullData.ys[4] = plane.Normal.Y;
|
||||
cullData.zs[4] = plane.Normal.Z;
|
||||
cullData.ds[4] = plane.D;
|
||||
|
||||
// Bottom
|
||||
plane = view.Frustum.GetBottom();
|
||||
cullData.xs[5] = plane.Normal.X;
|
||||
cullData.ys[5] = plane.Normal.Y;
|
||||
cullData.zs[5] = plane.Normal.Z;
|
||||
cullData.ds[5] = plane.D;
|
||||
|
||||
// Extra 0
|
||||
cullData.xs[6] = 0;
|
||||
cullData.ys[6] = 0;
|
||||
cullData.zs[6] = 0;
|
||||
cullData.ds[6] = 0;
|
||||
|
||||
// Extra 1
|
||||
cullData.xs[7] = 0;
|
||||
cullData.ys[7] = 0;
|
||||
cullData.zs[7] = 0;
|
||||
cullData.ds[7] = 0;
|
||||
}
|
||||
|
||||
float4 px = SIMD::Load(cullData.xs);
|
||||
float4 py = SIMD::Load(cullData.ys);
|
||||
float4 pz = SIMD::Load(cullData.zs);
|
||||
float4 pd = SIMD::Load(cullData.ds);
|
||||
float4 px2 = SIMD::Load(&cullData.xs[4]);
|
||||
float4 py2 = SIMD::Load(&cullData.ys[4]);
|
||||
float4 pz2 = SIMD::Load(&cullData.zs[4]);
|
||||
float4 pd2 = SIMD::Load(&cullData.ds[4]);
|
||||
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
const auto& sphere = actors[i]->GetSphere();
|
||||
float4 cx = SIMD::Splat(sphere.Center.X);
|
||||
float4 cy = SIMD::Splat(sphere.Center.Y);
|
||||
float4 cz = SIMD::Splat(sphere.Center.Z);
|
||||
float4 r = SIMD::Splat(-sphere.Radius);
|
||||
|
||||
float4 t = SIMD::Mul(cx, px);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz));
|
||||
t = SIMD::Add(t, pd);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
t = SIMD::Mul(cx, px2);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py2));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz2));
|
||||
t = SIMD::Add(t, pd2);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
actors[i]->Draw(renderContext);
|
||||
}
|
||||
#else
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
auto actor = actors[i];
|
||||
if (view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
|
||||
actor->Draw(renderContext);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
|
||||
{
|
||||
auto& view = renderContext.View;
|
||||
#if SCENE_RENDERING_USE_SIMD
|
||||
CullDataSIMD cullData;
|
||||
{
|
||||
// Near
|
||||
auto plane = view.Frustum.GetNear();
|
||||
cullData.xs[0] = plane.Normal.X;
|
||||
cullData.ys[0] = plane.Normal.Y;
|
||||
cullData.zs[0] = plane.Normal.Z;
|
||||
cullData.ds[0] = plane.D;
|
||||
|
||||
// Far
|
||||
plane = view.Frustum.GetFar();
|
||||
cullData.xs[1] = plane.Normal.X;
|
||||
cullData.ys[1] = plane.Normal.Y;
|
||||
cullData.zs[1] = plane.Normal.Z;
|
||||
cullData.ds[1] = plane.D;
|
||||
|
||||
// Left
|
||||
plane = view.Frustum.GetLeft();
|
||||
cullData.xs[2] = plane.Normal.X;
|
||||
cullData.ys[2] = plane.Normal.Y;
|
||||
cullData.zs[2] = plane.Normal.Z;
|
||||
cullData.ds[2] = plane.D;
|
||||
|
||||
// Right
|
||||
plane = view.Frustum.GetRight();
|
||||
cullData.xs[3] = plane.Normal.X;
|
||||
cullData.ys[3] = plane.Normal.Y;
|
||||
cullData.zs[3] = plane.Normal.Z;
|
||||
cullData.ds[3] = plane.D;
|
||||
|
||||
// Top
|
||||
plane = view.Frustum.GetTop();
|
||||
cullData.xs[4] = plane.Normal.X;
|
||||
cullData.ys[4] = plane.Normal.Y;
|
||||
cullData.zs[4] = plane.Normal.Z;
|
||||
cullData.ds[4] = plane.D;
|
||||
|
||||
// Bottom
|
||||
plane = view.Frustum.GetBottom();
|
||||
cullData.xs[5] = plane.Normal.X;
|
||||
cullData.ys[5] = plane.Normal.Y;
|
||||
cullData.zs[5] = plane.Normal.Z;
|
||||
cullData.ds[5] = plane.D;
|
||||
|
||||
// Extra 0
|
||||
cullData.xs[6] = 0;
|
||||
cullData.ys[6] = 0;
|
||||
cullData.zs[6] = 0;
|
||||
cullData.ds[6] = 0;
|
||||
|
||||
// Extra 1
|
||||
cullData.xs[7] = 0;
|
||||
cullData.ys[7] = 0;
|
||||
cullData.zs[7] = 0;
|
||||
cullData.ds[7] = 0;
|
||||
}
|
||||
|
||||
float4 px = SIMD::Load(cullData.xs);
|
||||
float4 py = SIMD::Load(cullData.ys);
|
||||
float4 pz = SIMD::Load(cullData.zs);
|
||||
float4 pd = SIMD::Load(cullData.ds);
|
||||
float4 px2 = SIMD::Load(&cullData.xs[4]);
|
||||
float4 py2 = SIMD::Load(&cullData.ys[4]);
|
||||
float4 pz2 = SIMD::Load(&cullData.zs[4]);
|
||||
float4 pd2 = SIMD::Load(&cullData.ds[4]);
|
||||
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
const auto& sphere = actors[i]->GetSphere();
|
||||
float4 cx = SIMD::Splat(sphere.Center.X);
|
||||
float4 cy = SIMD::Splat(sphere.Center.Y);
|
||||
float4 cz = SIMD::Splat(sphere.Center.Z);
|
||||
float4 r = SIMD::Splat(-sphere.Radius);
|
||||
|
||||
float4 t = SIMD::Mul(cx, px);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz));
|
||||
t = SIMD::Add(t, pd);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
t = SIMD::Mul(cx, px2);
|
||||
t = SIMD::Add(t, SIMD::Mul(cy, py2));
|
||||
t = SIMD::Add(t, SIMD::Mul(cz, pz2));
|
||||
t = SIMD::Add(t, pd2);
|
||||
t = SIMD::Sub(t, r);
|
||||
if (SIMD::MoveMask(t))
|
||||
continue;
|
||||
|
||||
if (actors[i]->GetStaticFlags() & renderContext.View.StaticFlagsMask)
|
||||
actors[i]->Draw(renderContext);
|
||||
}
|
||||
#else
|
||||
for (int32 i = 0; i < actors.Count(); i++)
|
||||
{
|
||||
auto actor = actors[i];
|
||||
if (actor->GetStaticFlags() & view.StaticFlagsMask && view.RenderLayersMask.HasLayer(actor->GetLayer()) && frustum.Intersects(actor->GetSphere()))
|
||||
actor->Draw(renderContext);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneRendering::Draw(RenderContext& renderContext)
|
||||
{
|
||||
// Skip if disabled
|
||||
@@ -248,13 +321,12 @@ void SceneRendering::Draw(RenderContext& renderContext)
|
||||
auto& view = renderContext.View;
|
||||
|
||||
// Draw all visual components
|
||||
const BoundingFrustum frustum = view.CullingFrustum;
|
||||
if (view.IsOfflinePass)
|
||||
{
|
||||
CullAndDrawOffline(frustum, renderContext, Geometry);
|
||||
Geometry.CullAndDrawOffline(renderContext);
|
||||
if (view.Pass & DrawPass::GBuffer)
|
||||
{
|
||||
CullAndDrawOffline(frustum, renderContext, Common);
|
||||
Common.CullAndDrawOffline(renderContext);
|
||||
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
|
||||
{
|
||||
auto actor = CommonNoCulling[i];
|
||||
@@ -265,15 +337,21 @@ void SceneRendering::Draw(RenderContext& renderContext)
|
||||
}
|
||||
else
|
||||
{
|
||||
CullAndDraw(frustum, renderContext, Geometry);
|
||||
Geometry.CullAndDraw(renderContext);
|
||||
if (view.Pass & DrawPass::GBuffer)
|
||||
{
|
||||
CullAndDraw(frustum, renderContext, Common);
|
||||
Common.CullAndDraw(renderContext);
|
||||
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
|
||||
{
|
||||
auto actor = CommonNoCulling[i];
|
||||
if (view.RenderLayersMask.HasLayer(actor->GetLayer()))
|
||||
{
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
PROFILE_CPU();
|
||||
___tracy_scoped_zone.Name(*actor->GetName(), actor->GetName().Length());
|
||||
#endif
|
||||
actor->Draw(renderContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,6 +372,9 @@ void SceneRendering::Draw(RenderContext& renderContext)
|
||||
|
||||
void SceneRendering::CollectPostFxVolumes(RenderContext& renderContext)
|
||||
{
|
||||
#if SCENE_RENDERING_USE_PROFILER
|
||||
PROFILE_CPU();
|
||||
#endif
|
||||
for (int32 i = 0; i < PostFxProviders.Count(); i++)
|
||||
{
|
||||
PostFxProviders[i]->Collect(renderContext);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "Engine/Core/Delegate.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Math/BoundingSphere.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Level/Types.h"
|
||||
|
||||
class SceneRenderTask;
|
||||
@@ -14,7 +16,7 @@ struct RenderView;
|
||||
/// <summary>
|
||||
/// Interface for actors that can override the default rendering settings (eg. PostFxVolume actor).
|
||||
/// </summary>
|
||||
class IPostFxSettingsProvider
|
||||
class FLAXENGINE_API IPostFxSettingsProvider
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -35,24 +37,37 @@ public:
|
||||
/// <summary>
|
||||
/// Scene rendering helper subsystem that boosts the level rendering by providing efficient objects cache and culling implementation.
|
||||
/// </summary>
|
||||
class SceneRendering
|
||||
class FLAXENGINE_API SceneRendering
|
||||
{
|
||||
friend Scene;
|
||||
|
||||
#if USE_EDITOR
|
||||
typedef Function<void(RenderView&)> PhysicsDebugCallback;
|
||||
friend class ViewportIconsRendererService;
|
||||
#endif
|
||||
struct DrawEntry
|
||||
{
|
||||
Actor* Actor;
|
||||
uint32 LayerMask;
|
||||
BoundingSphere Bounds;
|
||||
};
|
||||
|
||||
struct DrawEntries
|
||||
{
|
||||
Array<DrawEntry> List;
|
||||
|
||||
int32 Add(Actor* obj);
|
||||
void Update(Actor* obj, int32 key);
|
||||
void Remove(Actor* obj, int32 key);
|
||||
void Clear();
|
||||
void CullAndDraw(RenderContext& renderContext);
|
||||
void CullAndDrawOffline(RenderContext& renderContext);
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// Private data
|
||||
Scene* Scene;
|
||||
|
||||
public:
|
||||
|
||||
// Things to draw
|
||||
Array<Actor*> Geometry;
|
||||
Array<Actor*> Common;
|
||||
DrawEntries Geometry;
|
||||
DrawEntries Common;
|
||||
Array<Actor*> CommonNoCulling;
|
||||
Array<IPostFxSettingsProvider*> PostFxProviders;
|
||||
#if USE_EDITOR
|
||||
@@ -83,24 +98,36 @@ public:
|
||||
|
||||
public:
|
||||
|
||||
FORCE_INLINE void AddGeometry(Actor* obj)
|
||||
FORCE_INLINE int32 AddGeometry(Actor* obj)
|
||||
{
|
||||
Geometry.Add(obj);
|
||||
return Geometry.Add(obj);
|
||||
}
|
||||
|
||||
FORCE_INLINE void RemoveGeometry(Actor* obj)
|
||||
FORCE_INLINE void UpdateGeometry(Actor* obj, int32 key)
|
||||
{
|
||||
Geometry.Remove(obj);
|
||||
Geometry.Update(obj, key);
|
||||
}
|
||||
|
||||
FORCE_INLINE void AddCommon(Actor* obj)
|
||||
FORCE_INLINE void RemoveGeometry(Actor* obj, int32& key)
|
||||
{
|
||||
Common.Add(obj);
|
||||
Geometry.Remove(obj, key);
|
||||
key = -1;
|
||||
}
|
||||
|
||||
FORCE_INLINE void RemoveCommon(Actor* obj)
|
||||
FORCE_INLINE int32 AddCommon(Actor* obj)
|
||||
{
|
||||
Common.Remove(obj);
|
||||
return Common.Add(obj);
|
||||
}
|
||||
|
||||
FORCE_INLINE void UpdateCommon(Actor* obj, int32 key)
|
||||
{
|
||||
Common.Update(obj, key);
|
||||
}
|
||||
|
||||
FORCE_INLINE void RemoveCommon(Actor* obj, int32& key)
|
||||
{
|
||||
Common.Remove(obj, key);
|
||||
key = -1;
|
||||
}
|
||||
|
||||
FORCE_INLINE void AddCommonNoCulling(Actor* obj)
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
#include "Scene.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
|
||||
SceneTicking::TickData::TickData(int32 capacity)
|
||||
: Scripts(capacity)
|
||||
, Ticks(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
void SceneTicking::TickData::AddScript(Script* script)
|
||||
{
|
||||
Scripts.Add(script);
|
||||
@@ -22,6 +28,65 @@ void SceneTicking::TickData::RemoveScript(Script* script)
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneTicking::TickData::RemoveTick(void* callee)
|
||||
{
|
||||
for (int32 i = 0; i < Ticks.Count(); i++)
|
||||
{
|
||||
if (Ticks[i].Callee == callee)
|
||||
{
|
||||
Ticks.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTicking::TickData::Tick()
|
||||
{
|
||||
TickScripts(Scripts);
|
||||
|
||||
for (int32 i = 0; i < Ticks.Count(); i++)
|
||||
Ticks[i].Call();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void SceneTicking::TickData::RemoveTickExecuteInEditor(void* callee)
|
||||
{
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
{
|
||||
if (TicksExecuteInEditor[i].Callee == callee)
|
||||
{
|
||||
TicksExecuteInEditor.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTicking::TickData::TickExecuteInEditor()
|
||||
{
|
||||
TickScripts(ScriptsExecuteInEditor);
|
||||
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
TicksExecuteInEditor[i].Call();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void SceneTicking::TickData::Clear()
|
||||
{
|
||||
Scripts.Clear();
|
||||
Ticks.Clear();
|
||||
#if USE_EDITOR
|
||||
ScriptsExecuteInEditor.Clear();
|
||||
TicksExecuteInEditor.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
SceneTicking::FixedUpdateTickData::FixedUpdateTickData()
|
||||
: TickData(512)
|
||||
{
|
||||
}
|
||||
|
||||
void SceneTicking::FixedUpdateTickData::TickScripts(const Array<Script*>& scripts)
|
||||
{
|
||||
for (auto* script : scripts)
|
||||
@@ -30,6 +95,11 @@ void SceneTicking::FixedUpdateTickData::TickScripts(const Array<Script*>& script
|
||||
}
|
||||
}
|
||||
|
||||
SceneTicking::UpdateTickData::UpdateTickData()
|
||||
: TickData(1024)
|
||||
{
|
||||
}
|
||||
|
||||
void SceneTicking::UpdateTickData::TickScripts(const Array<Script*>& scripts)
|
||||
{
|
||||
for (auto* script : scripts)
|
||||
@@ -38,6 +108,11 @@ void SceneTicking::UpdateTickData::TickScripts(const Array<Script*>& scripts)
|
||||
}
|
||||
}
|
||||
|
||||
SceneTicking::LateUpdateTickData::LateUpdateTickData()
|
||||
: TickData(64)
|
||||
{
|
||||
}
|
||||
|
||||
void SceneTicking::LateUpdateTickData::TickScripts(const Array<Script*>& scripts)
|
||||
{
|
||||
for (auto* script : scripts)
|
||||
@@ -46,15 +121,9 @@ void SceneTicking::LateUpdateTickData::TickScripts(const Array<Script*>& scripts
|
||||
}
|
||||
}
|
||||
|
||||
SceneTicking::SceneTicking(::Scene* scene)
|
||||
: Scene(scene)
|
||||
{
|
||||
}
|
||||
|
||||
void SceneTicking::AddScript(Script* obj)
|
||||
{
|
||||
ASSERT(obj && obj->GetParent() && obj->GetParent()->GetScene() == Scene);
|
||||
|
||||
ASSERT_LOW_LAYER(obj && obj->GetParent() && obj->GetParent()->GetScene());
|
||||
if (obj->_tickFixedUpdate)
|
||||
FixedUpdate.AddScript(obj);
|
||||
if (obj->_tickUpdate)
|
||||
@@ -65,8 +134,7 @@ void SceneTicking::AddScript(Script* obj)
|
||||
|
||||
void SceneTicking::RemoveScript(Script* obj)
|
||||
{
|
||||
ASSERT(obj && obj->GetParent() && obj->GetParent()->GetScene() == Scene);
|
||||
|
||||
ASSERT_LOW_LAYER(obj && obj->GetParent() && obj->GetParent()->GetScene());
|
||||
if (obj->_tickFixedUpdate)
|
||||
FixedUpdate.RemoveScript(obj);
|
||||
if (obj->_tickUpdate)
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
/// </summary>
|
||||
class FLAXENGINE_API SceneTicking
|
||||
{
|
||||
friend Scene;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
@@ -28,13 +26,9 @@ public:
|
||||
(static_cast<T*>(callee)->*Method)();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void* Callee;
|
||||
SignatureObj FunctionObj;
|
||||
|
||||
public:
|
||||
|
||||
template<class T, void(T::*Method)()>
|
||||
void Bind(T* callee)
|
||||
{
|
||||
@@ -42,15 +36,15 @@ public:
|
||||
FunctionObj = &MethodCaller<T, Method>;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the binded function.
|
||||
/// </summary>
|
||||
FORCE_INLINE void Call() const
|
||||
{
|
||||
(*FunctionObj)(Callee);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Ticking data container.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API TickData
|
||||
{
|
||||
public:
|
||||
@@ -62,32 +56,10 @@ public:
|
||||
Array<Tick> TicksExecuteInEditor;
|
||||
#endif
|
||||
|
||||
TickData(int32 capacity)
|
||||
: Scripts(capacity)
|
||||
, Ticks(capacity)
|
||||
{
|
||||
}
|
||||
TickData(int32 capacity);
|
||||
|
||||
virtual void TickScripts(const Array<Script*>& scripts) = 0;
|
||||
|
||||
void Tick()
|
||||
{
|
||||
TickScripts(Scripts);
|
||||
|
||||
for (int32 i = 0; i < Ticks.Count(); i++)
|
||||
Ticks[i].Call();
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
void TickExecuteInEditor()
|
||||
{
|
||||
TickScripts(ScriptsExecuteInEditor);
|
||||
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
TicksExecuteInEditor[i].Call();
|
||||
}
|
||||
#endif
|
||||
|
||||
void AddScript(Script* script);
|
||||
void RemoveScript(Script* script);
|
||||
|
||||
@@ -99,17 +71,8 @@ public:
|
||||
Ticks.Add(tick);
|
||||
}
|
||||
|
||||
void RemoveTick(void* callee)
|
||||
{
|
||||
for (int32 i = 0; i < Ticks.Count(); i++)
|
||||
{
|
||||
if (Ticks[i].Callee == callee)
|
||||
{
|
||||
Ticks.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void RemoveTick(void* callee);
|
||||
void Tick();
|
||||
|
||||
#if USE_EDITOR
|
||||
template<class T, void(T::*Method)()>
|
||||
@@ -120,39 +83,18 @@ public:
|
||||
TicksExecuteInEditor.Add(tick);
|
||||
}
|
||||
|
||||
void RemoveTickExecuteInEditor(void* callee)
|
||||
{
|
||||
for (int32 i = 0; i < TicksExecuteInEditor.Count(); i++)
|
||||
{
|
||||
if (TicksExecuteInEditor[i].Callee == callee)
|
||||
{
|
||||
TicksExecuteInEditor.RemoveAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void RemoveTickExecuteInEditor(void* callee);
|
||||
void TickExecuteInEditor();
|
||||
#endif
|
||||
|
||||
void Clear()
|
||||
{
|
||||
Scripts.Clear();
|
||||
Ticks.Clear();
|
||||
#if USE_EDITOR
|
||||
ScriptsExecuteInEditor.Clear();
|
||||
TicksExecuteInEditor.Clear();
|
||||
#endif
|
||||
}
|
||||
void Clear();
|
||||
};
|
||||
|
||||
class FLAXENGINE_API FixedUpdateTickData : public TickData
|
||||
{
|
||||
public:
|
||||
|
||||
FixedUpdateTickData()
|
||||
: TickData(512)
|
||||
{
|
||||
}
|
||||
|
||||
FixedUpdateTickData();
|
||||
void TickScripts(const Array<Script*>& scripts) override;
|
||||
};
|
||||
|
||||
@@ -160,11 +102,7 @@ public:
|
||||
{
|
||||
public:
|
||||
|
||||
UpdateTickData()
|
||||
: TickData(1024)
|
||||
{
|
||||
}
|
||||
|
||||
UpdateTickData();
|
||||
void TickScripts(const Array<Script*>& scripts) override;
|
||||
};
|
||||
|
||||
@@ -172,20 +110,10 @@ public:
|
||||
{
|
||||
public:
|
||||
|
||||
LateUpdateTickData()
|
||||
: TickData(64)
|
||||
{
|
||||
}
|
||||
|
||||
LateUpdateTickData();
|
||||
void TickScripts(const Array<Script*>& scripts) override;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Scene* Scene;
|
||||
|
||||
explicit SceneTicking(::Scene* scene);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -156,14 +156,14 @@ void NavMesh::OnEnable()
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
|
||||
GetScene()->NavigationMeshes.Add(this);
|
||||
GetScene()->Navigation.Meshes.Add(this);
|
||||
AddTiles();
|
||||
}
|
||||
|
||||
void NavMesh::OnDisable()
|
||||
{
|
||||
RemoveTiles();
|
||||
GetScene()->NavigationMeshes.Remove(this);
|
||||
GetScene()->Navigation.Meshes.Remove(this);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -37,12 +37,12 @@ void NavMeshBoundsVolume::OnEnable()
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
|
||||
GetScene()->NavigationVolumes.Add(this);
|
||||
GetScene()->Navigation.Volumes.Add(this);
|
||||
}
|
||||
|
||||
void NavMeshBoundsVolume::OnDisable()
|
||||
{
|
||||
GetScene()->NavigationVolumes.Remove(this);
|
||||
GetScene()->Navigation.Volumes.Remove(this);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
|
||||
@@ -348,9 +348,9 @@ bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, floa
|
||||
// Check if any navmesh volume intersects with the tile
|
||||
bool foundAnyVolume = false;
|
||||
Vector2 rangeY;
|
||||
for (int32 i = 0; i < scene->NavigationVolumes.Count(); i++)
|
||||
for (int32 i = 0; i < scene->Navigation.Volumes.Count(); i++)
|
||||
{
|
||||
const auto volume = scene->NavigationVolumes[i];
|
||||
const auto volume = scene->Navigation.Volumes[i];
|
||||
if (!volume->AgentsMask.IsNavMeshSupported(navMesh->Properties))
|
||||
continue;
|
||||
const auto& volumeBounds = volume->GetBox();
|
||||
@@ -925,7 +925,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild
|
||||
for (auto& navMeshProperties : settings->NavMeshes)
|
||||
{
|
||||
NavMesh* navMesh = nullptr;
|
||||
for (auto e : scene->NavigationMeshes)
|
||||
for (auto e : scene->Navigation.Meshes)
|
||||
{
|
||||
if (e->Properties.Name == navMeshProperties.Name)
|
||||
{
|
||||
@@ -953,7 +953,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild
|
||||
}
|
||||
|
||||
// Build all navmeshes on the scene
|
||||
for (NavMesh* navMesh : scene->NavigationMeshes)
|
||||
for (NavMesh* navMesh : scene->Navigation.Meshes)
|
||||
{
|
||||
BuildDirtyBounds(scene, navMesh, dirtyBounds, rebuild);
|
||||
}
|
||||
@@ -961,7 +961,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild
|
||||
// Remove unused navmeshes
|
||||
if (settings->AutoRemoveMissingNavMeshes)
|
||||
{
|
||||
for (NavMesh* navMesh : scene->NavigationMeshes)
|
||||
for (NavMesh* navMesh : scene->Navigation.Meshes)
|
||||
{
|
||||
// Skip used navmeshes
|
||||
if (navMesh->Data.Tiles.HasItems())
|
||||
@@ -987,7 +987,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild
|
||||
void BuildWholeScene(Scene* scene)
|
||||
{
|
||||
// Compute total navigation area bounds
|
||||
const BoundingBox worldBounds = scene->GetNavigationBounds();
|
||||
const BoundingBox worldBounds = scene->Navigation.GetNavigationBounds();
|
||||
|
||||
BuildDirtyBounds(scene, worldBounds, true);
|
||||
}
|
||||
@@ -995,7 +995,7 @@ void BuildWholeScene(Scene* scene)
|
||||
void ClearNavigation(Scene* scene)
|
||||
{
|
||||
const bool autoRemoveMissingNavMeshes = NavigationSettings::Get()->AutoRemoveMissingNavMeshes;
|
||||
for (NavMesh* navMesh : scene->NavigationMeshes)
|
||||
for (NavMesh* navMesh : scene->Navigation.Meshes)
|
||||
{
|
||||
navMesh->ClearData();
|
||||
if (autoRemoveMissingNavMeshes)
|
||||
@@ -1020,7 +1020,7 @@ void NavMeshBuilder::Update()
|
||||
continue;
|
||||
|
||||
// Early out if scene has no bounds volumes to define nav mesh area
|
||||
if (scene->NavigationVolumes.IsEmpty())
|
||||
if (scene->Navigation.Volumes.IsEmpty())
|
||||
{
|
||||
ClearNavigation(scene);
|
||||
continue;
|
||||
@@ -1042,7 +1042,7 @@ void NavMeshBuilder::Update()
|
||||
void NavMeshBuilder::Build(Scene* scene, float timeoutMs)
|
||||
{
|
||||
// Early out if scene is not using navigation
|
||||
if (scene->NavigationVolumes.IsEmpty())
|
||||
if (scene->Navigation.Volumes.IsEmpty())
|
||||
{
|
||||
ClearNavigation(scene);
|
||||
return;
|
||||
@@ -1073,7 +1073,7 @@ void NavMeshBuilder::Build(Scene* scene, float timeoutMs)
|
||||
void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs)
|
||||
{
|
||||
// Early out if scene is not using navigation
|
||||
if (scene->NavigationVolumes.IsEmpty())
|
||||
if (scene->Navigation.Volumes.IsEmpty())
|
||||
{
|
||||
ClearNavigation(scene);
|
||||
return;
|
||||
|
||||
@@ -371,7 +371,7 @@ void Navigation::DrawNavMesh()
|
||||
bool skip = false;
|
||||
for (auto scene : Level::Scenes)
|
||||
{
|
||||
for (auto e : scene->NavigationMeshes)
|
||||
for (auto e : scene->Navigation.Meshes)
|
||||
{
|
||||
if (e->Properties == navMesh->Properties)
|
||||
{
|
||||
|
||||
@@ -311,6 +311,8 @@ void ParticleEffect::UpdateBounds()
|
||||
|
||||
_box = bounds;
|
||||
BoundingSphere::FromBox(bounds, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void ParticleEffect::Sync()
|
||||
@@ -515,6 +517,12 @@ void ParticleEffect::OnDebugDrawSelected()
|
||||
|
||||
#endif
|
||||
|
||||
void ParticleEffect::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void ParticleEffect::Serialize(SerializeStream& stream, const void* otherObj)
|
||||
{
|
||||
// Base
|
||||
@@ -691,7 +699,7 @@ void ParticleEffect::EndPlay()
|
||||
void ParticleEffect::OnEnable()
|
||||
{
|
||||
GetScene()->Ticking.Update.AddTick<ParticleEffect, &ParticleEffect::Update>(this);
|
||||
GetSceneRendering()->AddGeometry(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
#if USE_EDITOR
|
||||
GetSceneRendering()->AddViewportIcon(this);
|
||||
GetScene()->Ticking.Update.AddTickExecuteInEditor<ParticleEffect, &ParticleEffect::UpdateExecuteInEditor>(this);
|
||||
@@ -707,7 +715,7 @@ void ParticleEffect::OnDisable()
|
||||
GetScene()->Ticking.Update.RemoveTickExecuteInEditor(this);
|
||||
GetSceneRendering()->RemoveViewportIcon(this);
|
||||
#endif
|
||||
GetSceneRendering()->RemoveGeometry(this);
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
GetScene()->Ticking.Update.RemoveTick(this);
|
||||
|
||||
// Base
|
||||
|
||||
@@ -184,6 +184,7 @@ private:
|
||||
uint64 _lastUpdateFrame;
|
||||
float _lastMinDstSqr;
|
||||
Matrix _world;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
uint32 _parametersVersion = 0; // Version number for _parameters to be in sync with Instance.ParametersVersion
|
||||
Array<ParticleEffectParameter> _parameters; // Cached for scripting API
|
||||
Array<ParameterOverride> _parametersOverrides; // Cached parameter modifications to be applied to the parameters
|
||||
@@ -388,6 +389,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -361,8 +361,6 @@ void RenderList::Clear()
|
||||
|
||||
void RenderList::AddDrawCall(DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals)
|
||||
{
|
||||
ASSERT_LOW_LAYER(drawCall.Geometry.IndexBuffer);
|
||||
|
||||
// Mix object mask with material mask
|
||||
const auto mask = (DrawPass)(drawModes & drawCall.Material->GetDrawModes());
|
||||
if (mask == DrawPass::None)
|
||||
|
||||
@@ -47,6 +47,8 @@ void Terrain::UpdateBounds()
|
||||
BoundingBox::Merge(_box, patch->_bounds, _box);
|
||||
}
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void Terrain::CacheNeighbors()
|
||||
@@ -752,7 +754,7 @@ RigidBody* Terrain::GetAttachedRigidBody() const
|
||||
|
||||
void Terrain::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddGeometry(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
GetSceneRendering()->AddPhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
|
||||
#endif
|
||||
@@ -763,7 +765,7 @@ void Terrain::OnEnable()
|
||||
|
||||
void Terrain::OnDisable()
|
||||
{
|
||||
GetSceneRendering()->RemoveGeometry(this);
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
#if TERRAIN_USE_PHYSICS_DEBUG
|
||||
GetSceneRendering()->RemovePhysicsDebug<Terrain, &Terrain::DrawPhysicsDebug>(this);
|
||||
#endif
|
||||
@@ -803,6 +805,8 @@ void Terrain::OnLayerChanged()
|
||||
Actor::OnLayerChanged();
|
||||
|
||||
UpdateLayerBits();
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void Terrain::OnActiveInTreeChanged()
|
||||
|
||||
@@ -53,6 +53,7 @@ private:
|
||||
char _collisionLod;
|
||||
byte _lodCount;
|
||||
uint16 _chunkSize;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
float _scaleInLightmap;
|
||||
float _lodDistribution;
|
||||
Vector3 _boundsExtent;
|
||||
@@ -444,6 +445,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
@@ -455,7 +457,6 @@ protected:
|
||||
void OnEnable() override;
|
||||
void OnDisable() override;
|
||||
void OnTransformChanged() override;
|
||||
void OnLayerChanged() override;
|
||||
void OnActiveInTreeChanged() override;
|
||||
void BeginPlay(SceneBeginData* data) override;
|
||||
void EndPlay() override;
|
||||
|
||||
@@ -170,6 +170,12 @@ void SpriteRender::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
|
||||
_paramColor->SetValue(_color);
|
||||
}
|
||||
|
||||
void SpriteRender::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
void SpriteRender::OnEndPlay()
|
||||
{
|
||||
// Base
|
||||
@@ -187,7 +193,7 @@ void SpriteRender::OnEndPlay()
|
||||
|
||||
void SpriteRender::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddGeometry(this);
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
@@ -195,7 +201,7 @@ void SpriteRender::OnEnable()
|
||||
|
||||
void SpriteRender::OnDisable()
|
||||
{
|
||||
GetSceneRendering()->RemoveGeometry(this);
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
@@ -211,4 +217,6 @@ void SpriteRender::OnTransformChanged()
|
||||
_transform.GetWorld(world);
|
||||
BoundingSphere::Transform(localSphere, world, _sphere);
|
||||
BoundingBox::FromSphere(_sphere, _box);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ private:
|
||||
MaterialParameter* _paramImageMAD = nullptr;
|
||||
MaterialParameter* _paramColor = nullptr;
|
||||
AssetReference<Asset> _quadModel;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
public:
|
||||
|
||||
@@ -97,6 +98,7 @@ public:
|
||||
void DrawGeneric(RenderContext& renderContext) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
void OnLayerChanged() override;
|
||||
void OnEndPlay() override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -329,6 +329,8 @@ void TextRender::UpdateLayout()
|
||||
_localBox = box;
|
||||
BoundingBox::Transform(_localBox, _world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
bool TextRender::HasContentLoaded() const
|
||||
@@ -421,6 +423,12 @@ void TextRender::OnDebugDrawSelected()
|
||||
|
||||
#endif
|
||||
|
||||
void TextRender::OnLayerChanged()
|
||||
{
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
bool TextRender::IntersectsItself(const Ray& ray, float& distance, Vector3& normal)
|
||||
{
|
||||
#if USE_PRECISE_MESH_INTERSECTS
|
||||
@@ -480,8 +488,6 @@ void TextRender::Deserialize(DeserializeStream& stream, ISerializeModifier* modi
|
||||
|
||||
void TextRender::OnEnable()
|
||||
{
|
||||
GetSceneRendering()->AddGeometry(this);
|
||||
|
||||
// Base
|
||||
Actor::OnEnable();
|
||||
|
||||
@@ -489,6 +495,7 @@ void TextRender::OnEnable()
|
||||
{
|
||||
UpdateLayout();
|
||||
}
|
||||
_sceneRenderingKey = GetSceneRendering()->AddGeometry(this);
|
||||
}
|
||||
|
||||
void TextRender::OnDisable()
|
||||
@@ -498,7 +505,7 @@ void TextRender::OnDisable()
|
||||
_isLocalized = false;
|
||||
Localization::LocalizationChanged.Unbind<TextRender, &TextRender::UpdateLayout>(this);
|
||||
}
|
||||
GetSceneRendering()->RemoveGeometry(this);
|
||||
GetSceneRendering()->RemoveGeometry(this, _sceneRenderingKey);
|
||||
|
||||
// Base
|
||||
Actor::OnDisable();
|
||||
@@ -512,4 +519,6 @@ void TextRender::OnTransformChanged()
|
||||
_transform.GetWorld(_world);
|
||||
BoundingBox::Transform(_localBox, _world, _box);
|
||||
BoundingSphere::FromBox(_box, _sphere);
|
||||
if (_sceneRenderingKey != -1)
|
||||
GetSceneRendering()->UpdateGeometry(this, _sceneRenderingKey);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ private:
|
||||
Color _color;
|
||||
TextLayoutOptions _layoutOptions;
|
||||
int32 _size;
|
||||
int32 _sceneRenderingKey = -1;
|
||||
|
||||
BoundingBox _localBox;
|
||||
Matrix _world;
|
||||
@@ -169,6 +170,7 @@ public:
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
bool IntersectsItself(const Ray& ray, float& distance, Vector3& normal) override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
|
||||
|
||||
Reference in New Issue
Block a user