You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "Lightmap.h"
#include "Scene.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Content/Content.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/Scene/SceneLightmapsData.h"
#if USE_EDITOR
#include "Engine/ContentImporters/ImportTexture.h"
#include "Engine/ContentImporters/AssetsImportingManager.h"
#include "Engine/Graphics/Textures/TextureData.h"
#endif
#include "Engine/Serialization/Serialization.h"
void LightmapSettings::Serialize(SerializeStream& stream, const void* otherObj)
{
SERIALIZE_GET_OTHER_OBJ(LightmapSettings);
SERIALIZE(IndirectLightingIntensity);
SERIALIZE(GlobalObjectsScale);
SERIALIZE(ChartsPadding);
SERIALIZE(AtlasSize);
SERIALIZE(BounceCount);
SERIALIZE(CompressLightmaps);
SERIALIZE(UseGeometryWithNoMaterials);
SERIALIZE(Quality);
}
void LightmapSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
DESERIALIZE(IndirectLightingIntensity);
DESERIALIZE(GlobalObjectsScale);
DESERIALIZE(ChartsPadding);
DESERIALIZE(AtlasSize);
DESERIALIZE(BounceCount);
DESERIALIZE(CompressLightmaps);
DESERIALIZE(UseGeometryWithNoMaterials);
DESERIALIZE(Quality);
}
Lightmap::Lightmap(SceneLightmapsData* manager, int32 index, const SavedLightmapInfo& info)
: _manager(manager)
, _index(index)
{
// Try to load textures with given IDs
_textures[0] = Content::LoadAsync<Texture>(info.Lightmap0);
_textures[1] = Content::LoadAsync<Texture>(info.Lightmap1);
_textures[2] = Content::LoadAsync<Texture>(info.Lightmap2);
}
void Lightmap::UpdateTexture(Texture* texture, int32 index)
{
auto& prev = _textures[index];
if (prev.Get() != texture)
{
LOG(Info, "Changing lightmap {0} texture {1} from '{2}' to '{3}'", _index, index, prev ? prev->ToString() : String::Empty, texture ? texture->ToString() : String::Empty);
prev = texture;
}
}
void Lightmap::EnsureSize(int32 size)
{
ASSERT(size >= 4 && size <= 4096);
#if USE_EDITOR
_size = size;
#endif
// Check every texture
for (int32 textureIndex = 0; textureIndex < ARRAY_COUNT(_textures); textureIndex++)
{
auto& texture = _textures[textureIndex];
// Check if has texture linked
if (texture)
{
// Wait for the loading
if (texture->WaitForLoaded())
{
// Unlink texture that cannot be loaded
LOG(Warning, "Lightmap::EnsureSize failed to load texture");
texture.Unlink();
}
else
{
// Check if need to resize texture
if (texture->GetTexture()->Width() != size || texture->GetTexture()->Height() != size)
{
// Unlink texture and import new with valid size
LOG(Info, "Changing lightmap {0}:{1} size from {2} to {3}", _index, textureIndex, texture->GetTexture()->Size(), size);
texture.Unlink();
}
}
}
// Check if has missing texture
if (texture == nullptr)
{
#if USE_EDITOR
#if COMPILE_WITH_ASSETS_IMPORTER
Guid id = Guid::New();
LOG(Info, "Cannot load lightmap {0} ({1}:{2}). Creating new one.", id, _index, textureIndex);
String assetPath;
_manager->GetCachedLightmapPath(&assetPath, _index, textureIndex);
// Import texture with custom options
ImportTexture::Options options;
options.Type = TextureFormatType::HdrRGBA;
options.IndependentChannels = true;
options.Compress = _manager->GetScene()->GetLightmapSettings().CompressLightmaps;
options.IsAtlas = false;
options.sRGB = false;
options.NeverStream = false;
options.InternalLoad.Bind<Lightmap, &Lightmap::OnInitLightmap>(this);
if (AssetsImportingManager::Create(AssetsImportingManager::CreateTextureTag, assetPath, id, &options))
{
LOG(Error, "Cannot import empty lightmap {0}:{1}", _index, textureIndex);
}
auto result = Content::LoadAsync<Texture>(id);
#else
auto result = nullptr;
#endif
if (result == nullptr)
{
LOG(Error, "Cannot load new lightmap {0}:{1}", _index, textureIndex);
}
// Update asset
texture = result;
#else
LOG(Warning, "Cannot create empty lightmap. Saving data to the cooked content is disabled.");
#endif
}
}
}
#if USE_EDITOR
bool Lightmap::OnInitLightmap(TextureData& image)
{
// Initialize with transparent image
image.Width = image.Height = _size;
image.Depth = 1;
image.Format = PixelFormat::R8G8B8A8_UNorm;
image.Items.Resize(1);
image.Items[0].Mips.Resize(1);
auto& mip = image.Items[0].Mips[0];
mip.RowPitch = image.Width * 4;
mip.DepthPitch = mip.RowPitch * image.Height;
mip.Lines = image.Height;
mip.Data.Allocate(mip.DepthPitch);
Platform::MemoryClear(mip.Data.Get(), mip.DepthPitch);
return false;
}
#endif

View File

@@ -0,0 +1,113 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Renderer/Lightmaps.h"
class SceneLightmapsData;
/// <summary>
/// Shadows Of Mordor static light map
/// </summary>
class Lightmap
{
private:
SceneLightmapsData* _manager;
int32 _index;
#if USE_EDITOR
int32 _size;
#endif
AssetReference<Texture> _textures[3];
public:
/// <summary>
/// Initializes a new instance of the <see cref="Lightmap"/> class.
/// </summary>
/// <param name="manager">The manager.</param>
/// <param name="index">The index.</param>
/// <param name="info">The information.</param>
Lightmap(SceneLightmapsData* manager, int32 index, const SavedLightmapInfo& info);
public:
/// <summary>
/// Gets attached texture objects
/// </summary>
/// <param name="lightmap0">Lightmap 0 texture</param>
/// <param name="lightmap1">Lightmap 1 texture</param>
/// <param name="lightmap2">Lightmap 2 texture</param>
void GetTextures(GPUTexture** lightmap0, GPUTexture** lightmap1, GPUTexture** lightmap2) const
{
*lightmap0 = _textures[0] ? _textures[0]->GetTexture() : nullptr;
*lightmap1 = _textures[1] ? _textures[1]->GetTexture() : nullptr;
*lightmap2 = _textures[2] ? _textures[2]->GetTexture() : nullptr;
}
/// <summary>
/// Gets attached texture objects
/// </summary>
/// <param name="lightmaps">Lightmaps textures array</param>
void GetTextures(GPUTexture* lightmaps[]) const
{
lightmaps[0] = _textures[0] ? _textures[0]->GetTexture() : nullptr;
lightmaps[1] = _textures[1] ? _textures[1]->GetTexture() : nullptr;
lightmaps[2] = _textures[2] ? _textures[2]->GetTexture() : nullptr;
}
/// <summary>
/// Gets attached texture objects
/// </summary>
/// <param name="lightmaps">Lightmaps textures array</param>
void GetTextures(Texture* lightmaps[]) const
{
lightmaps[0] = _textures[0].Get();
lightmaps[1] = _textures[1].Get();
lightmaps[2] = _textures[2].Get();
}
/// <summary>
/// Gets lightmap info
/// </summary>
/// <param name="info">Lightmap info</param>
void GetInfo(SavedLightmapInfo& info) const
{
info.Lightmap0 = _textures[0].GetID();
info.Lightmap1 = _textures[1].GetID();
info.Lightmap2 = _textures[2].GetID();
}
/// <summary>
/// Update texture (change it to another asset)
/// </summary>
/// <param name="texture">New lightmap texture asset</param>
/// <param name="index">Texture index</param>
void UpdateTexture(Texture* texture, int32 index);
/// <summary>
/// Ensure that all textures have that size, if not resizing is performed
/// </summary>
/// <param name="size">Target size per texture</param>
void EnsureSize(int32 size);
/// <summary>
/// Determines whether this lightmap is ready (textures can be used by the renderer).
/// </summary>
/// <returns>True if lightmap textures are ready to use by renderer, otherwise false.</returns>
FORCE_INLINE bool IsReady() const
{
// TODO: link for events and cache this to be a boolean value
return _textures[0] && _textures[0]->GetTexture()->ResidentMipLevels() > 0
&& _textures[1] && _textures[1]->GetTexture()->ResidentMipLevels() > 0
&& _textures[2] && _textures[2]->GetTexture()->ResidentMipLevels() > 0;
}
private:
#if USE_EDITOR
bool OnInitLightmap(class TextureData& image);
#endif
};

View File

@@ -0,0 +1,335 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "Scene.h"
#include "Engine/Level/Level.h"
#include "Engine/Content/AssetInfo.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Factories/JsonAssetFactory.h"
#include "Engine/Physics/Colliders/MeshCollider.h"
#include "Engine/Level/Actors/StaticModel.h"
#include "Engine/Level/ActorsCache.h"
#include "Engine/Navigation/NavigationScene.h"
#include "Engine/Serialization/Serialization.h"
REGISTER_JSON_ASSET(SceneAsset, "FlaxEngine.SceneAsset");
SceneAsset::SceneAsset(const SpawnParams& params, const AssetInfo* info)
: JsonAsset(params, info)
{
}
#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)
, Navigation(::New<NavigationScene>(this))
{
// Default name
_name = TEXT("Scene");
// Link events
CSGData.CollisionData.Changed.Bind<Scene, &Scene::OnCsgCollisionDataChanged>(this);
CSGData.Model.Changed.Bind<Scene, &Scene::OnCsgModelChanged>(this);
#if COMPILE_WITH_CSG_BUILDER
CSGData.PostCSGBuild.Bind<Scene, &Scene::OnCSGBuildEnd>(this);
#endif
}
Scene::~Scene()
{
Delete(Navigation);
}
LightmapSettings Scene::GetLightmapSettings() const
{
return Info.LightmapSettings;
}
void Scene::SetLightmapSettings(const LightmapSettings& value)
{
Info.LightmapSettings = value;
}
void Scene::ClearLightmaps()
{
LightmapsData.ClearLightmaps();
}
void Scene::BuildCSG(float timeoutMs)
{
CSGData.BuildCSG(timeoutMs);
}
#if USE_EDITOR
String Scene::GetPath() const
{
AssetInfo info;
if (Content::GetAssetInfo(GetID(), info))
{
return info.Path;
}
return String::Empty;
}
String Scene::GetFilename() const
{
return StringUtils::GetFileNameWithoutExtension(GetPath());
}
String Scene::GetDataFolderPath() const
{
return Globals::ProjectContentFolder / TEXT("SceneData") / GetFilename();
}
#endif
MeshCollider* Scene::TryGetCsgCollider()
{
MeshCollider* result = nullptr;
for (int32 i = 0; i < Children.Count(); i++)
{
const auto collider = dynamic_cast<MeshCollider*>(Children[i]);
if (collider && collider->GetName() == CSG_COLLIDER_NAME)
{
result = collider;
break;
}
}
return result;
}
StaticModel* Scene::TryGetCsgModel()
{
StaticModel* result = nullptr;
for (int32 i = 0; i < Children.Count(); i++)
{
const auto model = dynamic_cast<StaticModel*>(Children[i]);
if (model && model->GetName() == CSG_MODEL_NAME)
{
result = model;
break;
}
}
return result;
}
void Scene::CreateCsgCollider()
{
// Create collider
auto result = New<MeshCollider>();
result->SetStaticFlags(StaticFlags::FullyStatic);
result->SetName(CSG_COLLIDER_NAME);
result->CollisionData = CSGData.CollisionData;
result->HideFlags |= HideFlags::DontSelect;
// Link it
if (IsDuringPlay())
{
result->SetParent(this, false);
}
else
{
result->_parent = this;
result->_scene = this;
Children.Add(result);
result->CreateManaged();
}
}
void Scene::CreateCsgModel()
{
// Create model
auto result = New<StaticModel>();
result->SetStaticFlags(StaticFlags::FullyStatic);
result->SetName(CSG_MODEL_NAME);
result->Model = CSGData.Model;
result->HideFlags |= HideFlags::DontSelect;
// Link it
if (IsDuringPlay())
{
result->SetParent(this, false);
}
else
{
result->_parent = this;
result->_scene = this;
Children.Add(result);
result->CreateManaged();
}
}
void Scene::OnCsgCollisionDataChanged()
{
// Use it only in play mode
if (!IsDuringPlay())
return;
// Try to find CSG collider
auto collider = TryGetCsgCollider();
if (collider)
{
// Update the collision asset
collider->CollisionData = CSGData.CollisionData;
}
else if (CSGData.CollisionData)
{
// Create collider
CreateCsgCollider();
}
}
void Scene::OnCsgModelChanged()
{
// Use it only in play mode
if (!IsDuringPlay())
return;
// Try to find CSG model
auto model = TryGetCsgModel();
if (model)
{
// Update the model asset
model->Model = CSGData.Model;
}
else if (CSGData.Model)
{
// Create model
CreateCsgModel();
}
}
void Scene::Serialize(SerializeStream& stream, const void* otherObj)
{
// Base
Actor::Serialize(stream, otherObj);
SERIALIZE_GET_OTHER_OBJ(Scene);
// Update scene info object
SaveTime = DateTime::NowUTC();
#if USE_EDITOR
// Save navmesh tiles to asset (if modified)
if (Navigation->IsDataDirty)
Navigation->SaveNavMesh();
#endif
LightmapsData.SaveLightmaps(Info.Lightmaps);
Info.Serialize(stream, other ? &other->Info : nullptr);
if (CSGData.HasData())
{
stream.JKEY("CSG");
stream.Object(&CSGData, other ? &other->CSGData : nullptr);
}
SERIALIZE_MEMBER(NavMesh, Navigation->DataAsset);
}
void Scene::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
// Base
Actor::Deserialize(stream, modifier);
Info.Deserialize(stream, modifier);
LightmapsData.LoadLightmaps(Info.Lightmaps);
CSGData.DeserializeIfExists(stream, "CSG", modifier);
DESERIALIZE_MEMBER(NavMesh, Navigation->DataAsset);
}
void Scene::OnDeleteObject()
{
// Cleanup
LightmapsData.UnloadLightmaps();
CSGData.Model.Unlink();
CSGData.CollisionData.Unlink();
// Base
Actor::OnDeleteObject();
}
void Scene::PostLoad()
{
// Initialize
_parent = nullptr;
_scene = this;
// Base
Actor::PostLoad();
}
void Scene::PostSpawn()
{
// Initialize
_parent = nullptr;
_scene = this;
// Base
Actor::PostSpawn();
}
void Scene::BeginPlay(SceneBeginData* data)
{
// Base
Actor::BeginPlay(data);
// Check if has CSG collision and create collider if need to (before play mode enter)
if (CSGData.CollisionData)
{
const auto collider = TryGetCsgCollider();
if (collider == nullptr)
CreateCsgCollider();
}
// Check if has CSG model and create model if need to (before play mode enter)
if (CSGData.Model)
{
const auto model = TryGetCsgModel();
if (model == nullptr)
CreateCsgModel();
}
}
void Scene::EndPlay()
{
// Improve scene cleanup performance by removing all data from scene rendering and ticking containers
Ticking.Clear();
Rendering.Clear();
// Base
Actor::EndPlay();
}
void Scene::OnEnable()
{
// Base
Actor::OnEnable();
Navigation->OnEnable();
}
void Scene::OnDisable()
{
Navigation->OnDisable();
// Base
Actor::OnDisable();
}
void Scene::OnTransformChanged()
{
// Base
Actor::OnTransformChanged();
_box = BoundingBox(_transform.Translation, _transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
}

View File

@@ -0,0 +1,171 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "../Actor.h"
#include "../SceneInfo.h"
#include "Engine/Content/JsonAsset.h"
#include "SceneLightmapsData.h"
#include "SceneCSGData.h"
#include "SceneRendering.h"
#include "SceneTicking.h"
class MeshCollider;
class Level;
class NavigationScene;
class ReloadScriptsAction;
/// <summary>
/// The scene root object that contains a hierarchy of actors.
/// </summary>
API_CLASS() class FLAXENGINE_API Scene final : public Actor
{
DECLARE_SCENE_OBJECT(Scene);
friend Level;
friend ReloadScriptsAction;
public:
/// <summary>
/// Finalizes an instance of the <see cref="Scene"/> class.
/// </summary>
~Scene();
public:
/// <summary>
/// The scene metadata.
/// </summary>
SceneInfo Info;
/// <summary>
/// The last load time.
/// </summary>
DateTime LoadTime;
/// <summary>
/// The last save time.
/// </summary>
DateTime SaveTime;
public:
/// <summary>
/// The scene rendering manager.
/// </summary>
SceneRendering Rendering;
/// <summary>
/// The scene ticking manager.
/// </summary>
SceneTicking Ticking;
/// <summary>
/// The static light manager for this scene.
/// </summary>
SceneLightmapsData LightmapsData;
/// <summary>
/// The CSG data container for this scene.
/// </summary>
CSG::SceneCSGData CSGData;
/// <summary>
/// The navigation scene (always valid).
/// </summary>
NavigationScene* Navigation;
/// <summary>
/// Gets the lightmap settings (per scene).
/// </summary>
API_PROPERTY(Attributes="EditorDisplay(\"Lightmap Settings\", EditorDisplayAttribute.InlineStyle)")
LightmapSettings GetLightmapSettings() const;
/// <summary>
/// Sets the lightmap settings (per scene).
/// </summary>
API_PROPERTY() void SetLightmapSettings(const LightmapSettings& value);
public:
/// <summary>
/// Removes all baked lightmap textures from the scene.
/// </summary>
API_FUNCTION() void ClearLightmaps();
/// <summary>
/// Builds the CSG geometry for the given scene.
/// </summary>
/// <remarks>Requests are enqueued till the next game scripts update.</remarks>
/// <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>
/// Gets path to the scene file
/// </summary>
API_PROPERTY() String GetPath() const;
/// <summary>
/// Gets filename of the scene file
/// </summary>
API_PROPERTY() String GetFilename() const;
/// <summary>
/// Gets path to the scene data folder
/// </summary>
API_PROPERTY() String GetDataFolderPath() const;
#endif
private:
MeshCollider* TryGetCsgCollider();
StaticModel* TryGetCsgModel();
void CreateCsgCollider();
void CreateCsgModel();
void OnCsgCollisionDataChanged();
void OnCsgModelChanged();
#if COMPILE_WITH_CSG_BUILDER
void OnCSGBuildEnd()
{
if (CSGData.CollisionData && TryGetCsgCollider() == nullptr)
CreateCsgCollider();
if (CSGData.Model && TryGetCsgModel() == nullptr)
CreateCsgModel();
}
#endif
public:
// [Actor]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
void OnDeleteObject() override;
void EndPlay() override;
protected:
// [Scene]
void PostLoad() override;
void PostSpawn() override;
void BeginPlay(SceneBeginData* data) override;
void OnEnable() override;
void OnDisable() override;
void OnTransformChanged() override;
};
/// <summary>
/// The scene asset.
/// </summary>
API_CLASS(NoSpawn) class SceneAsset : public JsonAsset
{
DECLARE_ASSET_HEADER(SceneAsset);
protected:
bool IsInternalType() const override
{
return true;
}
};

View File

@@ -0,0 +1,165 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "SceneCSGData.h"
#include "Engine/CSG/CSGBuilder.h"
#include "Engine/Serialization/Serialization.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/RawDataAsset.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Physics/CollisionData.h"
using namespace CSG;
void Brush::OnBrushModified()
{
#if COMPILE_WITH_CSG_BUILDER
const auto scene = GetBrushScene();
if (scene && scene->IsDuringPlay())
{
Builder::OnBrushModified(this);
}
#endif
}
SceneCSGData::SceneCSGData(Scene* scene)
: _scene(scene)
, BuildTime(0)
{
Data.Loaded.Bind<SceneCSGData, &SceneCSGData::OnDataChanged>(this);
Data.Changed.Bind<SceneCSGData, &SceneCSGData::OnDataChanged>(this);
}
void SceneCSGData::BuildCSG(float timeoutMs) const
{
#if COMPILE_WITH_CSG_BUILDER
Builder::Build(_scene, timeoutMs);
#endif
}
bool SceneCSGData::HasData() const
{
return Model && Data;
}
bool SceneCSGData::SurfaceData::Intersects(const Ray& ray, float& distance, Vector3& normal)
{
bool result = false;
float minDistance = MAX_float;
Vector3 minDistanceNormal = Vector3::Up;
for (int32 i = 0; i < Triangles.Count(); i++)
{
auto& e = Triangles[i];
if (CollisionsHelper::RayIntersectsTriangle(ray, e.V0, e.V1, e.V2, distance, normal) && distance < minDistance)
{
minDistance = distance;
minDistanceNormal = normal;
result = true;
}
}
distance = minDistance;
normal = minDistanceNormal;
return result;
}
bool SceneCSGData::TryGetSurfaceData(const Guid& brushId, int32 brushSurfaceIndex, SurfaceData& outData)
{
if (Data == nullptr || !Data->IsLoaded() || Data->Data.IsEmpty())
{
// Missing data or not loaded
return false;
}
MemoryReadStream stream(Data->Data);
// Check if has missing cache
if (DataBrushLocations.IsEmpty())
{
int32 version;
stream.ReadInt32(&version);
if (version == 1)
{
int32 brushesCount;
stream.ReadInt32(&brushesCount);
if (brushesCount < 0)
{
// Invalid data
return false;
}
DataBrushLocations.EnsureCapacity((int32)(brushesCount * 4.0f));
for (int32 i = 0; i < brushesCount; i++)
{
Guid id;
int32 pos;
stream.Read(&id);
stream.ReadInt32(&pos);
DataBrushLocations.Add(id, pos);
}
}
else
{
// Unknown version
LOG(Warning, "Unknwon version for scene CSG surface data (or corrupted file).");
return false;
}
}
// Find brush data
int32 brushLocation;
if (!DataBrushLocations.TryGet(brushId, brushLocation))
{
// Brush not found
return false;
}
stream.SetPosition(brushLocation);
// Skip leading surfaces data
int32 trianglesCount;
while (brushSurfaceIndex-- > 0)
{
stream.ReadInt32(&trianglesCount);
if (trianglesCount < 0 || trianglesCount > 100)
{
// Invalid data
return false;
}
stream.Read<Triangle>(trianglesCount);
}
// Read surface data
stream.ReadInt32(&trianglesCount);
if (trianglesCount < 0 || trianglesCount > 100)
{
// Invalid data
return false;
}
outData.Triangles.Clear();
outData.Triangles.Add(stream.Read<Triangle>(trianglesCount), trianglesCount);
return true;
}
void SceneCSGData::OnDataChanged()
{
// Clear cache
DataBrushLocations.Clear();
}
void SceneCSGData::Serialize(SerializeStream& stream, const void* otherObj)
{
SERIALIZE_GET_OTHER_OBJ(SceneCSGData);
SERIALIZE(Model);
SERIALIZE(Data);
SERIALIZE(CollisionData);
}
void SceneCSGData::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
DESERIALIZE(Model);
DESERIALIZE(Data);
DESERIALIZE(CollisionData);
}

View File

@@ -0,0 +1,108 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#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"
class Scene;
namespace CSG
{
/// <summary>
/// CSG geometry data container (used per scene).
/// </summary>
class SceneCSGData : public ISerializable
{
private:
Scene* _scene;
public:
/// <summary>
/// Initializes a new instance of the <see cref="SceneCSGData"/> class.
/// </summary>
/// <param name="scene">The parent scene.</param>
SceneCSGData(Scene* scene);
public:
/// <summary>
/// CSG mesh building action time (registered by CSG::Builder, in UTC format). Invalid if not build by active engine instance.
/// </summary>
DateTime BuildTime;
/// <summary>
/// The model mesh (used for rendering).
/// </summary>
AssetReference<Model> Model;
/// <summary>
/// The CSG mesh raw data.
/// </summary>
AssetReference<RawDataAsset> Data;
/// <summary>
/// The CSG mesh collision data.
/// </summary>
AssetReference<CollisionData> CollisionData;
/// <summary>
/// The brush data locations lookup for faster searching though Data container.
/// </summary>
Dictionary<Guid, int32> DataBrushLocations;
/// <summary>
/// The post CSG build action. Called by the CSGBuilder after CSG mesh building end.
/// </summary>
Action PostCSGBuild;
public:
/// <summary>
/// Build CSG geometry for the given scene.
/// </summary>
/// <param name="timeoutMs">The timeout to wait before building CSG (in milliseconds).</param>
void BuildCSG(float timeoutMs = 50) const;
/// <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:
struct SurfaceData
{
Array<Triangle> Triangles;
bool Intersects(const Ray& ray, float& distance, Vector3& normal);
};
/// <summary>
/// Tries the get the brush surface data (may fail if data is missing or given brush surface doesn't result with any triangle).
/// </summary>
/// <param name="brushId">The brush identifier.</param>
/// <param name="brushSurfaceIndex">Index of the brush surface.</param>
/// <param name="outData">The output data.</param>
/// <returns>True if found data, otherwise false.</returns>
bool TryGetSurfaceData(const Guid& brushId, int32 brushSurfaceIndex, SurfaceData& outData);
private:
void OnDataChanged();
public:
// [ISerializable]
void Serialize(SerializeStream& stream, const void* otherObj) override;
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override;
};
};

View File

@@ -0,0 +1,116 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "SceneLightmapsData.h"
#include "Engine/Core/Log.h"
#include "Engine/Level/Level.h"
#include "Engine/Level/Scene/Scene.h"
#include "Engine/Level/Scene/Lightmap.h"
SceneLightmapsData::SceneLightmapsData(Scene* scene)
: _lightmaps(4)
, _scene(scene)
{
}
SceneLightmapsData::~SceneLightmapsData()
{
// Ensure that lightmaps has been released
ASSERT(_lightmaps.IsEmpty());
}
Lightmap* SceneLightmapsData::GetReadyLightmap(int32 index)
{
return index >= 0 && index < _lightmaps.Count() && _lightmaps[index]->IsReady() ? _lightmaps[index] : nullptr;
}
#if USE_EDITOR
void SceneLightmapsData::GetCacheFolder(String* result)
{
*result = _scene->GetDataFolderPath() / TEXT("Lightmaps");
}
void SceneLightmapsData::GetCachedLightmapPath(String* result, int32 lightmapIndex, int32 textureIndex)
{
String cacheFolder;
GetCacheFolder(&cacheFolder);
*result = cacheFolder / String::Format(TEXT("Lightmap{0:0>2}-{1}"), lightmapIndex, textureIndex) + ASSET_FILES_EXTENSION_WITH_DOT;
}
#endif
void SceneLightmapsData::ClearLightmaps()
{
UpdateLightmapsCollection(0, 0);
}
void SceneLightmapsData::LoadLightmaps(Array<SavedLightmapInfo>& lightmaps)
{
// Unload previous
UnloadLightmaps();
if (lightmaps.IsEmpty())
return;
LOG(Info, "Loading {0} lightmap(s)", lightmaps.Count());
for (int32 i = 0; i < lightmaps.Count(); i++)
{
_lightmaps.Add(New<Lightmap>(this, i, lightmaps[i]));
}
}
void SceneLightmapsData::UnloadLightmaps()
{
if (_lightmaps.HasItems())
{
LOG(Info, "Unloding {0} lightmap(s)", _lightmaps.Count());
_lightmaps.ClearDelete();
}
}
void SceneLightmapsData::SaveLightmaps(Array<SavedLightmapInfo>& lightmaps)
{
lightmaps.Resize(_lightmaps.Count(), false);
for (int32 i = 0; i < _lightmaps.Count(); i++)
{
_lightmaps[i]->GetInfo(lightmaps[i]);
}
}
void SceneLightmapsData::UpdateLightmapsCollection(int32 count, int32 size)
{
// Check if amount will change
if (_lightmaps.Count() != count)
{
LOG(Info, "Changing amount of lightmaps from {0} to {1}", _lightmaps.Count(), count);
// Remove too many entries
while (_lightmaps.Count() > count)
{
auto lightmap = _lightmaps[count];
Delete(lightmap);
_lightmaps.RemoveAt(count);
}
// Add missing entries
while (_lightmaps.Count() < count)
{
SavedLightmapInfo info;
info.Lightmap0 = Guid::Empty;
info.Lightmap1 = Guid::Empty;
info.Lightmap2 = Guid::Empty;
auto lightmap = New<Lightmap>(this, _lightmaps.Count(), info);
_lightmaps.Add(lightmap);
}
}
// Resize invalid size lightmaps
for (int32 i = 0; i < _lightmaps.Count(); i++)
{
_lightmaps[i]->EnsureSize(size);
}
}

View File

@@ -0,0 +1,116 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Level/SceneInfo.h"
class Scene;
class Lightmap;
/// <summary>
/// Shadows Of Mordor static lighting data container (used per scene).
/// </summary>
class SceneLightmapsData
{
private:
Array<Lightmap*> _lightmaps;
Scene* _scene;
public:
/// <summary>
/// Initializes a new instance of the <see cref="StaticLightManager"/> class.
/// </summary>
/// <param name="scene">The parent scene.</param>
SceneLightmapsData(Scene* scene);
/// <summary>
/// Finalizes an instance of the <see cref="StaticLightManager"/> class.
/// </summary>
~SceneLightmapsData();
public:
FORCE_INLINE Scene* GetScene() const
{
return _scene;
}
/// <summary>
/// Gets lightmap at index
/// </summary>
/// <param name="index">Lightmap index</param>
/// <returns>Lightmap or null if missing</returns>
FORCE_INLINE Lightmap* GetLightmap(int32 index)
{
return index >= 0 && index < _lightmaps.Count() ? _lightmaps[index] : nullptr;
}
/// <summary>
/// Gets loaded lightmap at index
/// </summary>
/// <param name="index">Lightmap index</param>
/// <returns>Lightmap or null if missing or not ready</returns>
Lightmap* GetReadyLightmap(int32 index);
/// <summary>
/// Gets lightmaps array
/// </summary>
/// <returns>Lightmaps</returns>
FORCE_INLINE const Array<Lightmap*>* GetLightmaps() const
{
return &_lightmaps;
}
public:
#if USE_EDITOR
/// <summary>
/// Gets path to the lightmaps cache folder
/// </summary>
/// <param name="result">Result path</param>
void GetCacheFolder(String* result);
/// <summary>
/// Gets name for lightmap texture asset
/// </summary>
/// <param name="result">Result path</param>
/// <param name="lightmapIndex">Lightmap index</param>
/// <param name="textureIndex">Lightmap texture index</param>
void GetCachedLightmapPath(String* result, int32 lightmapIndex, int32 textureIndex);
#endif
public:
/// <summary>
/// Clear baked lightmaps data
/// </summary>
void ClearLightmaps();
/// <summary>
/// Loads the lightmaps data.
/// </summary>
/// <param name="lightmaps">The serialized lightmaps info.</param>
void LoadLightmaps(Array<SavedLightmapInfo>& lightmaps);
/// <summary>
/// Unloads the lightmaps.
/// </summary>
void UnloadLightmaps();
/// <summary>
/// Saves the lightmaps data.
/// </summary>
/// <param name="lightmaps">The serialized lightmaps info.</param>
void SaveLightmaps(Array<SavedLightmapInfo>& lightmaps);
/// <summary>
/// Updates the lightmaps collection (capacity and lightmap textures size).
/// </summary>
/// <param name="count">The lightmaps count.</param>
/// <param name="size">The textures size.</param>
void UpdateLightmapsCollection(int32 count, int32 size);
};

View File

@@ -0,0 +1,309 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "SceneRendering.h"
#include "Scene.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Level/Actors/PostFxVolume.h"
#define SCENE_RENDERING_USE_SIMD 0
#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];
} ALIGN_END(16);
#endif
SceneRendering::SceneRendering(::Scene* scene)
: Scene(scene)
{
}
void CullAndDraw(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
{
#if SCENE_RENDERING_USE_SIMD
auto& view = renderContext.View;
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 (frustum.Intersects(actor->GetSphere()))
actor->Draw(renderContext);
}
#endif
}
void CullAndDrawOffline(const BoundingFrustum& frustum, RenderContext& renderContext, const Array<Actor*>& actors)
{
#if SCENE_RENDERING_USE_SIMD
auto& view = renderContext.View;
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() & renderContext.View.StaticFlagsMask && frustum.Intersects(actor->GetSphere()))
actor->Draw(renderContext);
}
#endif
}
void SceneRendering::Draw(RenderContext& renderContext)
{
// Skip if disabled
if (!Scene->GetIsActive())
return;
auto& view = renderContext.View;
// Draw all visual components
const BoundingFrustum frustum = view.CullingFrustum;
if (view.IsOfflinePass)
{
CullAndDrawOffline(frustum, renderContext, Geometry);
if (view.Pass & DrawPass::GBuffer)
{
CullAndDrawOffline(frustum, renderContext, Common);
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
{
auto actor = CommonNoCulling[i];
if (actor->GetStaticFlags() & view.StaticFlagsMask)
actor->Draw(renderContext);
}
}
}
else
{
CullAndDraw(frustum, renderContext, Geometry);
if (view.Pass & DrawPass::GBuffer)
{
CullAndDraw(frustum, renderContext, Common);
for (int32 i = 0; i < CommonNoCulling.Count(); i++)
{
auto actor = CommonNoCulling[i];
actor->Draw(renderContext);
}
}
}
#if USE_EDITOR
if (view.Pass & DrawPass::GBuffer)
{
// Draw physics shapes
if (view.Flags & ViewFlags::PhysicsDebug)
{
for (int32 i = 0; i < PhysicsDebug.Count(); i++)
{
PhysicsDebug[i](view);
}
}
}
#endif
}
void SceneRendering::CollectPostFxVolumes(RenderContext& renderContext)
{
for (int32 i = 0; i < PostFxProviders.Count(); i++)
{
PostFxProviders[i]->Collect(renderContext);
}
}
void SceneRendering::Clear()
{
Geometry.Clear();
Common.Clear();
CommonNoCulling.Clear();
#if USE_EDITOR
PhysicsDebug.Clear();
#endif
}

View File

@@ -0,0 +1,155 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Delegate.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Level/Types.h"
class SceneRenderTask;
struct PostProcessSettings;
struct RenderContext;
struct RenderView;
/// <summary>
/// Interface for actors that can override the default rendering settings (eg. PostFxVolume actor).
/// </summary>
class IPostFxSettingsProvider
{
public:
/// <summary>
/// Collects the settings for rendering of the specified task.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
virtual void Collect(RenderContext& renderContext) = 0;
/// <summary>
/// Blends the object settings to the given settings using given weight.
/// </summary>
/// <param name="other">The other settings to blend to.</param>
/// <param name="weight">The blending weight (normalized to 0-1 range).</param>
virtual void Blend(PostProcessSettings& other, float weight) = 0;
};
/// <summary>
/// Scene rendering helper subsystem that boosts the level rendering by providing efficient objects cache and culling implementation.
/// </summary>
class SceneRendering
{
friend Scene;
#if USE_EDITOR
typedef Function<void(RenderView&)> PhysicsDebugCallback;
#endif
private:
// Private data
Scene* Scene;
public:
// Things to draw
Array<Actor*> Geometry;
Array<Actor*> Common;
Array<Actor*> CommonNoCulling;
Array<IPostFxSettingsProvider*> PostFxProviders;
#if USE_EDITOR
Array<PhysicsDebugCallback> PhysicsDebug;
Array<Actor*> ViewportIcons;
#endif
explicit SceneRendering(::Scene* scene);
public:
/// <summary>
/// Draws the scene. Performs the optimized actors culling and draw calls submission for the current render pass (defined by the render view).
/// </summary>
/// <param name="renderContext">The rendering context.</param>
void Draw(RenderContext& renderContext);
/// <summary>
/// Collects the post fx volumes for the given rendering view.
/// </summary>
/// <param name="renderContext">The rendering context.</param>
void CollectPostFxVolumes(RenderContext& renderContext);
/// <summary>
/// Clears this instance data.
/// </summary>
void Clear();
public:
FORCE_INLINE void AddGeometry(Actor* obj)
{
Geometry.Add(obj);
}
FORCE_INLINE void RemoveGeometry(Actor* obj)
{
Geometry.Remove(obj);
}
FORCE_INLINE void AddCommon(Actor* obj)
{
Common.Add(obj);
}
FORCE_INLINE void RemoveCommon(Actor* obj)
{
Common.Remove(obj);
}
FORCE_INLINE void AddCommonNoCulling(Actor* obj)
{
CommonNoCulling.Add(obj);
}
FORCE_INLINE void RemoveCommonNoCulling(Actor* obj)
{
CommonNoCulling.Remove(obj);
}
FORCE_INLINE void AddPostFxProvider(IPostFxSettingsProvider* obj)
{
PostFxProviders.Add(obj);
}
FORCE_INLINE void RemovePostFxProvider(IPostFxSettingsProvider* obj)
{
PostFxProviders.Remove(obj);
}
#if USE_EDITOR
template<class T, void(T::*Method)(RenderView&)>
FORCE_INLINE void AddPhysicsDebug(T* obj)
{
PhysicsDebugCallback f;
f.Bind<T, Method>(obj);
PhysicsDebug.Add(f);
}
template<class T, void(T::*Method)(RenderView&)>
void RemovePhysicsDebug(T* obj)
{
PhysicsDebugCallback f;
f.Bind<T, Method>(obj);
PhysicsDebug.Remove(f);
}
FORCE_INLINE void AddViewportIcon(Actor* obj)
{
ViewportIcons.Add(obj);
}
FORCE_INLINE void RemoveViewportIcon(Actor* obj)
{
ViewportIcons.Remove(obj);
}
#endif
};

View File

@@ -0,0 +1,83 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "SceneTicking.h"
#include "Scene.h"
#include "Engine/Scripting/Script.h"
void SceneTicking::TickData::AddScript(Script* script)
{
Scripts.Add(script);
#if USE_EDITOR
if (script->_executeInEditor)
ScriptsExecuteInEditor.Add(script);
#endif
}
void SceneTicking::TickData::RemoveScript(Script* script)
{
Scripts.Remove(script);
#if USE_EDITOR
if (script->_executeInEditor)
ScriptsExecuteInEditor.Remove(script);
#endif
}
void SceneTicking::FixedUpdateTickData::TickScripts(const Array<Script*>& scripts)
{
for (auto* script : scripts)
{
script->OnFixedUpdate();
}
}
void SceneTicking::UpdateTickData::TickScripts(const Array<Script*>& scripts)
{
for (auto* script : scripts)
{
script->OnUpdate();
}
}
void SceneTicking::LateUpdateTickData::TickScripts(const Array<Script*>& scripts)
{
for (auto* script : scripts)
{
script->OnLateUpdate();
}
}
SceneTicking::SceneTicking(::Scene* scene)
: Scene(scene)
{
}
void SceneTicking::AddScript(Script* obj)
{
ASSERT(obj && obj->GetParent() && obj->GetParent()->GetScene() == Scene);
if (obj->_tickFixedUpdate)
FixedUpdate.AddScript(obj);
if (obj->_tickUpdate)
Update.AddScript(obj);
if (obj->_tickLateUpdate)
LateUpdate.AddScript(obj);
}
void SceneTicking::RemoveScript(Script* obj)
{
ASSERT(obj && obj->GetParent() && obj->GetParent()->GetScene() == Scene);
if (obj->_tickFixedUpdate)
FixedUpdate.RemoveScript(obj);
if (obj->_tickUpdate)
Update.RemoveScript(obj);
if (obj->_tickLateUpdate)
LateUpdate.RemoveScript(obj);
}
void SceneTicking::Clear()
{
FixedUpdate.Clear();
Update.Clear();
LateUpdate.Clear();
}

View File

@@ -0,0 +1,208 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Level/Types.h"
#include "Engine/Core/Collections/Array.h"
/// <summary>
/// Scene gameplay updating helper subsystem that boosts the level ticking by providing efficient objects cache.
/// </summary>
class SceneTicking
{
friend Scene;
public:
/// <summary>
/// Tick function type.
/// </summary>
class Tick
{
public:
/// <summary>
/// Signature of the function to call
/// </summary>
typedef void (*Signature)();
typedef void (*SignatureObj)(void*);
template<class T, void(T::*Method)()>
static void MethodCaller(void* callee)
{
(static_cast<T*>(callee)->*Method)();
}
public:
void* Callee;
SignatureObj FunctionObj;
public:
template<class T, void(T::*Method)()>
void Bind(T* callee)
{
Callee = callee;
FunctionObj = &MethodCaller<T, Method>;
}
/// <summary>
/// Calls the binded function.
/// </summary>
/// <returns>Function result</returns>
void Call() const
{
(*FunctionObj)(Callee);
}
};
class TickData
{
public:
Array<Script*> Scripts;
#if USE_EDITOR
Array<Script*> ScriptsExecuteInEditor;
#endif
Array<Tick> Ticks;
TickData(int32 capacity)
: Scripts(capacity)
, Ticks(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 TickEditorScripts()
{
TickScripts(ScriptsExecuteInEditor);
}
#endif
void AddScript(Script* script);
void RemoveScript(Script* script);
template<class T, void(T::*Method)()>
void AddTick(T* callee)
{
SceneTicking::Tick tick;
tick.Bind<T, Method>(callee);
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 Clear()
{
Scripts.Clear();
Ticks.Clear();
#if USE_EDITOR
ScriptsExecuteInEditor.Clear();
#endif
}
};
class FixedUpdateTickData : public TickData
{
public:
FixedUpdateTickData()
: TickData(512)
{
}
void TickScripts(const Array<Script*>& scripts) override;
};
class UpdateTickData : public TickData
{
public:
UpdateTickData()
: TickData(1024)
{
}
void TickScripts(const Array<Script*>& scripts) override;
};
class LateUpdateTickData : public TickData
{
public:
LateUpdateTickData()
: TickData(64)
{
}
void TickScripts(const Array<Script*>& scripts) override;
};
private:
Scene* Scene;
explicit SceneTicking(::Scene* scene);
public:
/// <summary>
/// Adds the script to scene ticking system.
/// </summary>
/// <param name="obj">The object.</param>
void AddScript(Script* obj);
/// <summary>
/// Removes the script from scene ticking system.
/// </summary>
/// <param name="obj">The object.</param>
void RemoveScript(Script* obj);
/// <summary>
/// Clears this instance data.
/// </summary>
void Clear();
public:
/// <summary>
/// The fixed update tick function.
/// </summary>
FixedUpdateTickData FixedUpdate;
/// <summary>
/// The update tick function.
/// </summary>
UpdateTickData Update;
/// <summary>
/// The late update tick function.
/// </summary>
LateUpdateTickData LateUpdate;
};