You're breathtaking!
This commit is contained in:
158
Source/Engine/Level/Scene/Lightmap.cpp
Normal file
158
Source/Engine/Level/Scene/Lightmap.cpp
Normal 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
|
||||
113
Source/Engine/Level/Scene/Lightmap.h
Normal file
113
Source/Engine/Level/Scene/Lightmap.h
Normal 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
|
||||
};
|
||||
335
Source/Engine/Level/Scene/Scene.cpp
Normal file
335
Source/Engine/Level/Scene/Scene.cpp
Normal 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);
|
||||
}
|
||||
171
Source/Engine/Level/Scene/Scene.h
Normal file
171
Source/Engine/Level/Scene/Scene.h
Normal 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;
|
||||
}
|
||||
};
|
||||
165
Source/Engine/Level/Scene/SceneCSGData.cpp
Normal file
165
Source/Engine/Level/Scene/SceneCSGData.cpp
Normal 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);
|
||||
}
|
||||
108
Source/Engine/Level/Scene/SceneCSGData.h
Normal file
108
Source/Engine/Level/Scene/SceneCSGData.h
Normal 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;
|
||||
};
|
||||
};
|
||||
116
Source/Engine/Level/Scene/SceneLightmapsData.cpp
Normal file
116
Source/Engine/Level/Scene/SceneLightmapsData.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
116
Source/Engine/Level/Scene/SceneLightmapsData.h
Normal file
116
Source/Engine/Level/Scene/SceneLightmapsData.h
Normal 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);
|
||||
};
|
||||
309
Source/Engine/Level/Scene/SceneRendering.cpp
Normal file
309
Source/Engine/Level/Scene/SceneRendering.cpp
Normal 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
|
||||
}
|
||||
155
Source/Engine/Level/Scene/SceneRendering.h
Normal file
155
Source/Engine/Level/Scene/SceneRendering.h
Normal 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
|
||||
};
|
||||
83
Source/Engine/Level/Scene/SceneTicking.cpp
Normal file
83
Source/Engine/Level/Scene/SceneTicking.cpp
Normal 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();
|
||||
}
|
||||
208
Source/Engine/Level/Scene/SceneTicking.h
Normal file
208
Source/Engine/Level/Scene/SceneTicking.h
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user